[SETUPAPI]
[reactos.git] / reactos / dll / win32 / setupapi / queue.c
1 /*
2 * Setupapi file queue routines
3 *
4 * Copyright 2002 Alexandre Julliard for CodeWeavers
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "setupapi_private.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
24
25 /* Unicode constants */
26 static const WCHAR DotSecurity[] = {'.','S','e','c','u','r','i','t','y',0};
27
28 /* context structure for the default queue callback */
29 struct default_callback_context
30 {
31 HWND owner;
32 HWND progress;
33 UINT message;
34 };
35
36 struct file_op
37 {
38 struct file_op *next;
39 UINT style;
40 WCHAR *src_root;
41 WCHAR *src_path;
42 WCHAR *src_file;
43 WCHAR *src_descr;
44 WCHAR *src_tag;
45 WCHAR *dst_path;
46 WCHAR *dst_file;
47 PSECURITY_DESCRIPTOR dst_sd;
48 };
49
50 struct file_op_queue
51 {
52 struct file_op *head;
53 struct file_op *tail;
54 unsigned int count;
55 };
56
57 struct file_queue
58 {
59 struct file_op_queue copy_queue;
60 struct file_op_queue delete_queue;
61 struct file_op_queue rename_queue;
62 DWORD flags;
63 };
64
65
66 static inline WCHAR *strdupW( const WCHAR *str )
67 {
68 WCHAR *ret = NULL;
69 if (str)
70 {
71 int len = (strlenW(str) + 1) * sizeof(WCHAR);
72 if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( ret, str, len );
73 }
74 return ret;
75 }
76
77 static inline char *strdupWtoA( const WCHAR *str )
78 {
79 char *ret = NULL;
80 if (str)
81 {
82 DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
83 if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
84 WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
85 }
86 return ret;
87 }
88
89 /* append a file operation to a queue */
90 static inline void queue_file_op( struct file_op_queue *queue, struct file_op *op )
91 {
92 op->next = NULL;
93 if (queue->tail) queue->tail->next = op;
94 else queue->head = op;
95 queue->tail = op;
96 queue->count++;
97 }
98
99 /* free all the file operations on a given queue */
100 static void free_file_op_queue( struct file_op_queue *queue )
101 {
102 struct file_op *t, *op = queue->head;
103
104 while( op )
105 {
106 HeapFree( GetProcessHeap(), 0, op->src_root );
107 HeapFree( GetProcessHeap(), 0, op->src_path );
108 HeapFree( GetProcessHeap(), 0, op->src_file );
109 HeapFree( GetProcessHeap(), 0, op->src_descr );
110 HeapFree( GetProcessHeap(), 0, op->src_tag );
111 HeapFree( GetProcessHeap(), 0, op->dst_path );
112 if (op->dst_sd) LocalFree( op->dst_sd);
113 if (op->dst_file != op->src_file) HeapFree( GetProcessHeap(), 0, op->dst_file );
114 t = op;
115 op = op->next;
116 HeapFree( GetProcessHeap(), 0, t );
117 }
118 }
119
120 /* concat 3 strings to make a path, handling separators correctly */
121 static void concat_W( WCHAR *buffer, const WCHAR *src1, const WCHAR *src2, const WCHAR *src3 )
122 {
123 *buffer = 0;
124 if (src1 && *src1)
125 {
126 strcpyW( buffer, src1 );
127 buffer += strlenW(buffer );
128 if (buffer[-1] != '\\') *buffer++ = '\\';
129 if (src2) while (*src2 == '\\') src2++;
130 }
131
132 if (src2)
133 {
134 strcpyW( buffer, src2 );
135 buffer += strlenW(buffer );
136 if (buffer[-1] != '\\') *buffer++ = '\\';
137 if (src3) while (*src3 == '\\') src3++;
138 }
139 if (src3)
140 {
141 strcpyW( buffer, src3 );
142 buffer += strlenW(buffer );
143 }
144 }
145
146
147 /***********************************************************************
148 * build_filepathsW
149 *
150 * Build a FILEPATHS_W structure for a given file operation.
151 */
152 static BOOL build_filepathsW( const struct file_op *op, FILEPATHS_W *paths )
153 {
154 unsigned int src_len = 1, dst_len = 1;
155 WCHAR *source = (PWSTR)paths->Source, *target = (PWSTR)paths->Target;
156
157 if (op->src_root) src_len += strlenW(op->src_root) + 1;
158 if (op->src_path) src_len += strlenW(op->src_path) + 1;
159 if (op->src_file) src_len += strlenW(op->src_file) + 1;
160 if (op->dst_path) dst_len += strlenW(op->dst_path) + 1;
161 if (op->dst_file) dst_len += strlenW(op->dst_file) + 1;
162 src_len *= sizeof(WCHAR);
163 dst_len *= sizeof(WCHAR);
164
165 if (!source || HeapSize( GetProcessHeap(), 0, source ) < src_len )
166 {
167 HeapFree( GetProcessHeap(), 0, source );
168 paths->Source = source = HeapAlloc( GetProcessHeap(), 0, src_len );
169 }
170 if (!target || HeapSize( GetProcessHeap(), 0, target ) < dst_len )
171 {
172 HeapFree( GetProcessHeap(), 0, target );
173 paths->Target = target = HeapAlloc( GetProcessHeap(), 0, dst_len );
174 }
175 if (!source || !target) return FALSE;
176 concat_W( source, op->src_root, op->src_path, op->src_file );
177 concat_W( target, NULL, op->dst_path, op->dst_file );
178 paths->Win32Error = 0;
179 paths->Flags = 0;
180 return TRUE;
181 }
182
183
184 /***********************************************************************
185 * QUEUE_callback_WtoA
186 *
187 * Map a file callback parameters from W to A and call the A callback.
188 */
189 UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification,
190 UINT_PTR param1, UINT_PTR param2 )
191 {
192 struct callback_WtoA_context *callback_ctx = context;
193 char buffer[MAX_PATH];
194 UINT ret;
195 UINT_PTR old_param2 = param2;
196
197 switch(notification)
198 {
199 case SPFILENOTIFY_COPYERROR:
200 param2 = (UINT_PTR)&buffer;
201 /* fall through */
202 case SPFILENOTIFY_STARTDELETE:
203 case SPFILENOTIFY_ENDDELETE:
204 case SPFILENOTIFY_DELETEERROR:
205 case SPFILENOTIFY_STARTRENAME:
206 case SPFILENOTIFY_ENDRENAME:
207 case SPFILENOTIFY_RENAMEERROR:
208 case SPFILENOTIFY_STARTCOPY:
209 case SPFILENOTIFY_ENDCOPY:
210 case SPFILENOTIFY_QUEUESCAN_EX:
211 {
212 FILEPATHS_W *pathsW = (FILEPATHS_W *)param1;
213 FILEPATHS_A pathsA;
214
215 pathsA.Source = strdupWtoA( pathsW->Source );
216 pathsA.Target = strdupWtoA( pathsW->Target );
217 pathsA.Win32Error = pathsW->Win32Error;
218 pathsA.Flags = pathsW->Flags;
219 ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification,
220 (UINT_PTR)&pathsA, param2 );
221 HeapFree( GetProcessHeap(), 0, (void *)pathsA.Source );
222 HeapFree( GetProcessHeap(), 0, (void *)pathsA.Target );
223 }
224 if (notification == SPFILENOTIFY_COPYERROR)
225 MultiByteToWideChar( CP_ACP, 0, buffer, -1, (WCHAR *)old_param2, MAX_PATH );
226 break;
227
228 case SPFILENOTIFY_STARTREGISTRATION:
229 case SPFILENOTIFY_ENDREGISTRATION:
230 {
231 SP_REGISTER_CONTROL_STATUSW *statusW = (SP_REGISTER_CONTROL_STATUSW *)param1;
232 SP_REGISTER_CONTROL_STATUSA statusA;
233
234 statusA.cbSize = sizeof(statusA);
235 statusA.FileName = strdupWtoA( statusW->FileName );
236 statusA.Win32Error = statusW->Win32Error;
237 statusA.FailureCode = statusW->FailureCode;
238 ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification,
239 (UINT_PTR)&statusA, param2 );
240 HeapFree( GetProcessHeap(), 0, (LPSTR)statusA.FileName );
241 }
242 break;
243
244 case SPFILENOTIFY_QUEUESCAN:
245 {
246 LPWSTR targetW = (LPWSTR)param1;
247 LPSTR target = strdupWtoA( targetW );
248
249 ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification,
250 (UINT_PTR)target, param2 );
251 HeapFree( GetProcessHeap(), 0, target );
252 }
253 break;
254
255 case SPFILENOTIFY_NEEDMEDIA:
256 FIXME("mapping for %d not implemented\n",notification);
257 case SPFILENOTIFY_STARTQUEUE:
258 case SPFILENOTIFY_ENDQUEUE:
259 case SPFILENOTIFY_STARTSUBQUEUE:
260 case SPFILENOTIFY_ENDSUBQUEUE:
261 default:
262 ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, param1, param2 );
263 break;
264 }
265 return ret;
266 }
267
268
269 /***********************************************************************
270 * get_src_file_info
271 *
272 * Retrieve the source file information for a given file.
273 */
274 static void get_src_file_info( HINF hinf, struct file_op *op )
275 {
276 static const WCHAR SourceDisksNames[] =
277 {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0};
278 static const WCHAR SourceDisksFiles[] =
279 {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0};
280
281 INFCONTEXT file_ctx, disk_ctx;
282 INT id, diskid;
283 DWORD len, len2;
284
285 /* find the SourceDisksFiles entry */
286 if (!SetupFindFirstLineW( hinf, SourceDisksFiles, op->src_file, &file_ctx ))
287 {
288 if ((op->style & (SP_COPY_SOURCE_ABSOLUTE|SP_COPY_SOURCEPATH_ABSOLUTE))) return;
289 /* no specific info, use .inf file source directory */
290 if (!op->src_root) op->src_root = PARSER_get_src_root( hinf );
291 return;
292 }
293 if (!SetupGetIntField( &file_ctx, 1, &diskid )) return;
294
295 /* now find the diskid in the SourceDisksNames section */
296 if (!SetupFindFirstLineW( hinf, SourceDisksNames, NULL, &disk_ctx )) return;
297 for (;;)
298 {
299 if (SetupGetIntField( &disk_ctx, 0, &id ) && (id == diskid)) break;
300 if (!SetupFindNextLine( &disk_ctx, &disk_ctx )) return;
301 }
302
303 /* and fill in the missing info */
304
305 if (!op->src_descr)
306 {
307 if (SetupGetStringFieldW( &disk_ctx, 1, NULL, 0, &len ) &&
308 (op->src_descr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) )))
309 SetupGetStringFieldW( &disk_ctx, 1, op->src_descr, len, NULL );
310 }
311 if (!op->src_tag)
312 {
313 if (SetupGetStringFieldW( &disk_ctx, 2, NULL, 0, &len ) &&
314 (op->src_tag = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) )))
315 SetupGetStringFieldW( &disk_ctx, 2, op->src_tag, len, NULL );
316 }
317 if (!op->src_path && !(op->style & SP_COPY_SOURCE_ABSOLUTE))
318 {
319 len = len2 = 0;
320 if (!(op->style & SP_COPY_SOURCEPATH_ABSOLUTE))
321 {
322 /* retrieve relative path for this disk */
323 if (!SetupGetStringFieldW( &disk_ctx, 4, NULL, 0, &len )) len = 0;
324 }
325 /* retrieve relative path for this file */
326 if (!SetupGetStringFieldW( &file_ctx, 2, NULL, 0, &len2 )) len2 = 0;
327
328 if ((len || len2) &&
329 (op->src_path = HeapAlloc( GetProcessHeap(), 0, (len+len2)*sizeof(WCHAR) )))
330 {
331 WCHAR *ptr = op->src_path;
332 if (len)
333 {
334 SetupGetStringFieldW( &disk_ctx, 4, op->src_path, len, NULL );
335 ptr = op->src_path + strlenW(op->src_path);
336 if (len2 && ptr > op->src_path && ptr[-1] != '\\') *ptr++ = '\\';
337 }
338 if (!SetupGetStringFieldW( &file_ctx, 2, ptr, len2, NULL )) *ptr = 0;
339 }
340 }
341 if (!op->src_root) op->src_root = PARSER_get_src_root(hinf);
342 }
343
344
345 /***********************************************************************
346 * get_destination_dir
347 *
348 * Retrieve the destination dir for a given section.
349 */
350 static WCHAR *get_destination_dir( HINF hinf, const WCHAR *section )
351 {
352 static const WCHAR Dest[] = {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0};
353 static const WCHAR Def[] = {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0};
354 INFCONTEXT context;
355
356 if (!SetupFindFirstLineW( hinf, Dest, section, &context ) &&
357 !SetupFindFirstLineW( hinf, Dest, Def, &context )) return NULL;
358 return PARSER_get_dest_dir( &context );
359 }
360
361
362 static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD );
363
364 /***********************************************************************
365 * extract_cabinet_file
366 *
367 * Extract a file from a .cab file.
368 */
369 static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root,
370 const WCHAR *src, const WCHAR *dst )
371 {
372 static const WCHAR extW[] = {'.','c','a','b',0};
373 static HMODULE advpack;
374
375 char *cab_path, *cab_file;
376 int len = strlenW( cabinet );
377
378 /* make sure the cabinet file has a .cab extension */
379 if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE;
380 if (!pExtractFiles)
381 {
382 if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" )))
383 {
384 ERR( "could not load advpack.dll\n" );
385 return FALSE;
386 }
387 if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles" )))
388 {
389 ERR( "could not find ExtractFiles in advpack.dll\n" );
390 return FALSE;
391 }
392 }
393
394 if (!(cab_path = strdupWtoA( root ))) return FALSE;
395 len = WideCharToMultiByte( CP_ACP, 0, cabinet, -1, NULL, 0, NULL, NULL );
396 if (!(cab_file = HeapAlloc( GetProcessHeap(), 0, strlen(cab_path) + len + 1 )))
397 {
398 HeapFree( GetProcessHeap(), 0, cab_path );
399 return FALSE;
400 }
401 strcpy( cab_file, cab_path );
402 if (cab_file[0] && cab_file[strlen(cab_file)-1] != '\\') strcat( cab_file, "\\" );
403 WideCharToMultiByte( CP_ACP, 0, cabinet, -1, cab_file + strlen(cab_file), len, NULL, NULL );
404 FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) );
405 pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 );
406 HeapFree( GetProcessHeap(), 0, cab_file );
407 HeapFree( GetProcessHeap(), 0, cab_path );
408 return CopyFileW( src, dst, FALSE /*FIXME*/ );
409 }
410
411
412 /***********************************************************************
413 * SetupOpenFileQueue (SETUPAPI.@)
414 */
415 HSPFILEQ WINAPI SetupOpenFileQueue(void)
416 {
417 struct file_queue *queue;
418
419 if (!(queue = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*queue))))
420 return INVALID_HANDLE_VALUE;
421 return queue;
422 }
423
424
425 /***********************************************************************
426 * SetupCloseFileQueue (SETUPAPI.@)
427 */
428 BOOL WINAPI SetupCloseFileQueue( HSPFILEQ handle )
429 {
430 struct file_queue *queue = handle;
431
432 free_file_op_queue( &queue->copy_queue );
433 free_file_op_queue( &queue->rename_queue );
434 free_file_op_queue( &queue->delete_queue );
435 HeapFree( GetProcessHeap(), 0, queue );
436 return TRUE;
437 }
438
439
440 /***********************************************************************
441 * SetupQueueCopyIndirectA (SETUPAPI.@)
442 */
443 BOOL WINAPI SetupQueueCopyIndirectA( PSP_FILE_COPY_PARAMS_A params )
444 {
445 struct file_queue *queue = params->QueueHandle;
446 struct file_op *op;
447
448 if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
449 op->style = params->CopyStyle;
450 op->src_root = strdupAtoW( params->SourceRootPath );
451 op->src_path = strdupAtoW( params->SourcePath );
452 op->src_file = strdupAtoW( params->SourceFilename );
453 op->src_descr = strdupAtoW( params->SourceDescription );
454 op->src_tag = strdupAtoW( params->SourceTagfile );
455 op->dst_path = strdupAtoW( params->TargetDirectory );
456 op->dst_file = strdupAtoW( params->TargetFilename );
457
458 /* some defaults */
459 if (!op->src_file) op->src_file = op->dst_file;
460 if (params->LayoutInf)
461 {
462 get_src_file_info( params->LayoutInf, op );
463 if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file );
464 }
465
466 TRACE( "root=%s path=%s file=%s -> dir=%s file=%s descr=%s tag=%s\n",
467 debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file),
468 debugstr_w(op->dst_path), debugstr_w(op->dst_file),
469 debugstr_w(op->src_descr), debugstr_w(op->src_tag) );
470
471 queue_file_op( &queue->copy_queue, op );
472 return TRUE;
473 }
474
475
476 /***********************************************************************
477 * SetupQueueCopyIndirectW (SETUPAPI.@)
478 */
479 BOOL WINAPI SetupQueueCopyIndirectW( PSP_FILE_COPY_PARAMS_W params )
480 {
481 struct file_queue *queue = params->QueueHandle;
482 struct file_op *op;
483
484 if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
485 op->style = params->CopyStyle;
486 op->src_root = strdupW( params->SourceRootPath );
487 op->src_path = strdupW( params->SourcePath );
488 op->src_file = strdupW( params->SourceFilename );
489 op->src_descr = strdupW( params->SourceDescription );
490 op->src_tag = strdupW( params->SourceTagfile );
491 op->dst_path = strdupW( params->TargetDirectory );
492 op->dst_file = strdupW( params->TargetFilename );
493 op->dst_sd = NULL;
494 if (params->SecurityDescriptor)
495 ConvertStringSecurityDescriptorToSecurityDescriptorW( params->SecurityDescriptor, SDDL_REVISION_1, &op->dst_sd, NULL );
496
497 /* some defaults */
498 if (!op->src_file) op->src_file = op->dst_file;
499 if (params->LayoutInf)
500 {
501 get_src_file_info( params->LayoutInf, op );
502 if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file );
503 }
504
505 TRACE( "root=%s path=%s file=%s -> dir=%s file=%s descr=%s tag=%s\n",
506 debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file),
507 debugstr_w(op->dst_path), debugstr_w(op->dst_file),
508 debugstr_w(op->src_descr), debugstr_w(op->src_tag) );
509
510 queue_file_op( &queue->copy_queue, op );
511 return TRUE;
512 }
513
514
515 /***********************************************************************
516 * SetupQueueCopyA (SETUPAPI.@)
517 */
518 BOOL WINAPI SetupQueueCopyA( HSPFILEQ queue, PCSTR src_root, PCSTR src_path, PCSTR src_file,
519 PCSTR src_descr, PCSTR src_tag, PCSTR dst_dir, PCSTR dst_file,
520 DWORD style )
521 {
522 SP_FILE_COPY_PARAMS_A params;
523
524 params.cbSize = sizeof(params);
525 params.QueueHandle = queue;
526 params.SourceRootPath = src_root;
527 params.SourcePath = src_path;
528 params.SourceFilename = src_file;
529 params.SourceDescription = src_descr;
530 params.SourceTagfile = src_tag;
531 params.TargetDirectory = dst_dir;
532 params.TargetFilename = dst_file;
533 params.CopyStyle = style;
534 params.LayoutInf = 0;
535 params.SecurityDescriptor = NULL;
536 return SetupQueueCopyIndirectA( &params );
537 }
538
539
540 /***********************************************************************
541 * SetupQueueCopyW (SETUPAPI.@)
542 */
543 BOOL WINAPI SetupQueueCopyW( HSPFILEQ queue, PCWSTR src_root, PCWSTR src_path, PCWSTR src_file,
544 PCWSTR src_descr, PCWSTR src_tag, PCWSTR dst_dir, PCWSTR dst_file,
545 DWORD style )
546 {
547 SP_FILE_COPY_PARAMS_W params;
548
549 params.cbSize = sizeof(params);
550 params.QueueHandle = queue;
551 params.SourceRootPath = src_root;
552 params.SourcePath = src_path;
553 params.SourceFilename = src_file;
554 params.SourceDescription = src_descr;
555 params.SourceTagfile = src_tag;
556 params.TargetDirectory = dst_dir;
557 params.TargetFilename = dst_file;
558 params.CopyStyle = style;
559 params.LayoutInf = 0;
560 params.SecurityDescriptor = NULL;
561 return SetupQueueCopyIndirectW( &params );
562 }
563
564
565 /***********************************************************************
566 * SetupQueueDefaultCopyA (SETUPAPI.@)
567 */
568 BOOL WINAPI SetupQueueDefaultCopyA( HSPFILEQ queue, HINF hinf, PCSTR src_root, PCSTR src_file,
569 PCSTR dst_file, DWORD style )
570 {
571 SP_FILE_COPY_PARAMS_A params;
572
573 params.cbSize = sizeof(params);
574 params.QueueHandle = queue;
575 params.SourceRootPath = src_root;
576 params.SourcePath = NULL;
577 params.SourceFilename = src_file;
578 params.SourceDescription = NULL;
579 params.SourceTagfile = NULL;
580 params.TargetDirectory = NULL;
581 params.TargetFilename = dst_file;
582 params.CopyStyle = style;
583 params.LayoutInf = hinf;
584 params.SecurityDescriptor = NULL;
585 return SetupQueueCopyIndirectA( &params );
586 }
587
588
589 /***********************************************************************
590 * SetupQueueDefaultCopyW (SETUPAPI.@)
591 */
592 BOOL WINAPI SetupQueueDefaultCopyW( HSPFILEQ queue, HINF hinf, PCWSTR src_root, PCWSTR src_file,
593 PCWSTR dst_file, DWORD style )
594 {
595 SP_FILE_COPY_PARAMS_W params;
596
597 params.cbSize = sizeof(params);
598 params.QueueHandle = queue;
599 params.SourceRootPath = src_root;
600 params.SourcePath = NULL;
601 params.SourceFilename = src_file;
602 params.SourceDescription = NULL;
603 params.SourceTagfile = NULL;
604 params.TargetDirectory = NULL;
605 params.TargetFilename = dst_file;
606 params.CopyStyle = style;
607 params.LayoutInf = hinf;
608 params.SecurityDescriptor = NULL;
609 return SetupQueueCopyIndirectW( &params );
610 }
611
612
613 /***********************************************************************
614 * SetupQueueDeleteA (SETUPAPI.@)
615 */
616 BOOL WINAPI SetupQueueDeleteA( HSPFILEQ handle, PCSTR part1, PCSTR part2 )
617 {
618 struct file_queue *queue = handle;
619 struct file_op *op;
620
621 if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
622 op->style = 0;
623 op->src_root = NULL;
624 op->src_path = NULL;
625 op->src_file = NULL;
626 op->src_descr = NULL;
627 op->src_tag = NULL;
628 op->dst_path = strdupAtoW( part1 );
629 op->dst_file = strdupAtoW( part2 );
630 queue_file_op( &queue->delete_queue, op );
631 return TRUE;
632 }
633
634
635 /***********************************************************************
636 * SetupQueueDeleteW (SETUPAPI.@)
637 */
638 BOOL WINAPI SetupQueueDeleteW( HSPFILEQ handle, PCWSTR part1, PCWSTR part2 )
639 {
640 struct file_queue *queue = handle;
641 struct file_op *op;
642
643 if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
644 op->style = 0;
645 op->src_root = NULL;
646 op->src_path = NULL;
647 op->src_file = NULL;
648 op->src_descr = NULL;
649 op->src_tag = NULL;
650 op->dst_path = strdupW( part1 );
651 op->dst_file = strdupW( part2 );
652 queue_file_op( &queue->delete_queue, op );
653 return TRUE;
654 }
655
656
657 /***********************************************************************
658 * SetupQueueRenameA (SETUPAPI.@)
659 */
660 BOOL WINAPI SetupQueueRenameA( HSPFILEQ handle, PCSTR SourcePath, PCSTR SourceFilename,
661 PCSTR TargetPath, PCSTR TargetFilename )
662 {
663 struct file_queue *queue = handle;
664 struct file_op *op;
665
666 if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
667 op->style = 0;
668 op->src_root = NULL;
669 op->src_path = strdupAtoW( SourcePath );
670 op->src_file = strdupAtoW( SourceFilename );
671 op->src_descr = NULL;
672 op->src_tag = NULL;
673 op->dst_path = strdupAtoW( TargetPath );
674 op->dst_file = strdupAtoW( TargetFilename );
675 queue_file_op( &queue->rename_queue, op );
676 return TRUE;
677 }
678
679
680 /***********************************************************************
681 * SetupQueueRenameW (SETUPAPI.@)
682 */
683 BOOL WINAPI SetupQueueRenameW( HSPFILEQ handle, PCWSTR SourcePath, PCWSTR SourceFilename,
684 PCWSTR TargetPath, PCWSTR TargetFilename )
685 {
686 struct file_queue *queue = handle;
687 struct file_op *op;
688
689 if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
690 op->style = 0;
691 op->src_root = NULL;
692 op->src_path = strdupW( SourcePath );
693 op->src_file = strdupW( SourceFilename );
694 op->src_descr = NULL;
695 op->src_tag = NULL;
696 op->dst_path = strdupW( TargetPath );
697 op->dst_file = strdupW( TargetFilename );
698 queue_file_op( &queue->rename_queue, op );
699 return TRUE;
700 }
701
702
703 /***********************************************************************
704 * SetupQueueCopySectionA (SETUPAPI.@)
705 */
706 BOOL WINAPI SetupQueueCopySectionA( HSPFILEQ queue, PCSTR src_root, HINF hinf, HINF hlist,
707 PCSTR section, DWORD style )
708 {
709 UNICODE_STRING sectionW;
710 BOOL ret = FALSE;
711
712 if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
713 {
714 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
715 return FALSE;
716 }
717 if (!src_root)
718 ret = SetupQueueCopySectionW( queue, NULL, hinf, hlist, sectionW.Buffer, style );
719 else
720 {
721 UNICODE_STRING srcW;
722 if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
723 {
724 ret = SetupQueueCopySectionW( queue, srcW.Buffer, hinf, hlist, sectionW.Buffer, style );
725 RtlFreeUnicodeString( &srcW );
726 }
727 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
728 }
729 RtlFreeUnicodeString( &sectionW );
730 return ret;
731 }
732
733
734 /***********************************************************************
735 * SetupQueueCopySectionW (SETUPAPI.@)
736 */
737 BOOL WINAPI SetupQueueCopySectionW( HSPFILEQ queue, PCWSTR src_root, HINF hinf, HINF hlist,
738 PCWSTR section, DWORD style )
739 {
740 SP_FILE_COPY_PARAMS_W params;
741 LPWSTR security_key, security_descriptor = NULL;
742 INFCONTEXT context, security_context;
743 WCHAR dest[MAX_PATH], src[MAX_PATH];
744 INT flags;
745 DWORD required;
746 BOOL ret;
747
748 TRACE( "hinf=%p/%p section=%s root=%s\n",
749 hinf, hlist, debugstr_w(section), debugstr_w(src_root) );
750
751 /* Check for .Security section */
752 security_key = MyMalloc( (strlenW( section ) + strlenW( DotSecurity )) * sizeof(WCHAR) + sizeof(UNICODE_NULL) );
753 if (!security_key)
754 {
755 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
756 return FALSE;
757 }
758 strcpyW( security_key, section );
759 strcatW( security_key, DotSecurity );
760 ret = SetupFindFirstLineW( hinf, security_key, NULL, &security_context );
761 MyFree(security_key);
762 if (ret)
763 {
764 if (!SetupGetLineTextW( &security_context, NULL, NULL, NULL, NULL, 0, &required ))
765 return FALSE;
766 security_descriptor = MyMalloc( required * sizeof(WCHAR) );
767 if (!security_descriptor)
768 {
769 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
770 return FALSE;
771 }
772 if (!SetupGetLineTextW( &security_context, NULL, NULL, NULL, security_descriptor, required, NULL ))
773 {
774 MyFree( security_descriptor );
775 return FALSE;
776 }
777 }
778
779 params.cbSize = sizeof(params);
780 params.QueueHandle = queue;
781 params.SourceRootPath = src_root;
782 params.SourcePath = NULL;
783 params.SourceDescription = NULL;
784 params.SourceTagfile = NULL;
785 params.TargetFilename = dest;
786 params.CopyStyle = style;
787 params.LayoutInf = hinf;
788 params.SecurityDescriptor = security_descriptor;
789
790 ret = FALSE;
791 if (!hlist) hlist = hinf;
792 if (!hinf) hinf = hlist;
793 if (!SetupFindFirstLineW( hlist, section, NULL, &context )) goto done;
794 if (!(params.TargetDirectory = get_destination_dir( hinf, section ))) goto done;
795 do
796 {
797 if (!SetupGetStringFieldW( &context, 1, dest, sizeof(dest)/sizeof(WCHAR), NULL ))
798 goto done;
799 if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) *src = 0;
800 if (!SetupGetIntField( &context, 4, &flags )) flags = 0; /* FIXME */
801
802 params.SourceFilename = *src ? src : NULL;
803 if (!SetupQueueCopyIndirectW( &params )) goto done;
804 } while (SetupFindNextLine( &context, &context ));
805 ret = TRUE;
806
807 done:
808 if (security_descriptor)
809 MyFree( security_descriptor );
810 return ret;
811 }
812
813
814 /***********************************************************************
815 * SetupQueueDeleteSectionA (SETUPAPI.@)
816 */
817 BOOL WINAPI SetupQueueDeleteSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
818 {
819 UNICODE_STRING sectionW;
820 BOOL ret = FALSE;
821
822 if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
823 {
824 ret = SetupQueueDeleteSectionW( queue, hinf, hlist, sectionW.Buffer );
825 RtlFreeUnicodeString( &sectionW );
826 }
827 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
828 return ret;
829 }
830
831
832 /***********************************************************************
833 * SetupQueueDeleteSectionW (SETUPAPI.@)
834 */
835 BOOL WINAPI SetupQueueDeleteSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
836 {
837 INFCONTEXT context;
838 WCHAR *dest_dir;
839 WCHAR buffer[MAX_PATH];
840 BOOL ret = FALSE;
841 INT flags;
842
843 TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
844
845 if (!hlist) hlist = hinf;
846 if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
847 if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
848 do
849 {
850 if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
851 goto done;
852 if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
853 if (!SetupQueueDeleteW( queue, dest_dir, buffer )) goto done;
854 } while (SetupFindNextLine( &context, &context ));
855
856 ret = TRUE;
857 done:
858 HeapFree( GetProcessHeap(), 0, dest_dir );
859 return ret;
860 }
861
862
863 /***********************************************************************
864 * SetupQueueRenameSectionA (SETUPAPI.@)
865 */
866 BOOL WINAPI SetupQueueRenameSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
867 {
868 UNICODE_STRING sectionW;
869 BOOL ret = FALSE;
870
871 if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
872 {
873 ret = SetupQueueRenameSectionW( queue, hinf, hlist, sectionW.Buffer );
874 RtlFreeUnicodeString( &sectionW );
875 }
876 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
877 return ret;
878 }
879
880
881 /***********************************************************************
882 * SetupQueueRenameSectionW (SETUPAPI.@)
883 */
884 BOOL WINAPI SetupQueueRenameSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
885 {
886 INFCONTEXT context;
887 WCHAR *dest_dir;
888 WCHAR src[MAX_PATH], dst[MAX_PATH];
889 BOOL ret = FALSE;
890
891 TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
892
893 if (!hlist) hlist = hinf;
894 if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
895 if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
896 do
897 {
898 if (!SetupGetStringFieldW( &context, 1, dst, sizeof(dst)/sizeof(WCHAR), NULL ))
899 goto done;
900 if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL ))
901 goto done;
902 if (!SetupQueueRenameW( queue, dest_dir, src, NULL, dst )) goto done;
903 } while (SetupFindNextLine( &context, &context ));
904
905 ret = TRUE;
906 done:
907 HeapFree( GetProcessHeap(), 0, dest_dir );
908 return ret;
909 }
910
911
912 /***********************************************************************
913 * SetupCommitFileQueueA (SETUPAPI.@)
914 */
915 BOOL WINAPI SetupCommitFileQueueA( HWND owner, HSPFILEQ queue, PSP_FILE_CALLBACK_A handler,
916 PVOID context )
917 {
918 struct callback_WtoA_context ctx;
919
920 ctx.orig_context = context;
921 ctx.orig_handler = handler;
922 return SetupCommitFileQueueW( owner, queue, QUEUE_callback_WtoA, &ctx );
923 }
924
925
926 /***********************************************************************
927 * create_full_pathW
928 *
929 * Recursively create all directories in the path.
930 */
931 static BOOL create_full_pathW(const WCHAR *path)
932 {
933 BOOL ret = TRUE;
934 int len;
935 WCHAR *new_path;
936
937 new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR));
938 strcpyW(new_path, path);
939
940 while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
941 new_path[len - 1] = 0;
942
943 while(!CreateDirectoryW(new_path, NULL))
944 {
945 WCHAR *slash;
946 DWORD last_error = GetLastError();
947
948 if(last_error == ERROR_ALREADY_EXISTS)
949 break;
950
951 if(last_error != ERROR_PATH_NOT_FOUND)
952 {
953 ret = FALSE;
954 break;
955 }
956
957 if(!(slash = strrchrW(new_path, '\\')))
958 {
959 ret = FALSE;
960 break;
961 }
962
963 len = slash - new_path;
964 new_path[len] = 0;
965 if(!create_full_pathW(new_path))
966 {
967 ret = FALSE;
968 break;
969 }
970 new_path[len] = '\\';
971 }
972
973 HeapFree(GetProcessHeap(), 0, new_path);
974 return ret;
975 }
976
977 static BOOL do_file_copyW( LPCWSTR source, LPCWSTR target, DWORD style,
978 PSP_FILE_CALLBACK_W handler, PVOID context )
979 {
980 BOOL rc = FALSE;
981 BOOL docopy = TRUE;
982
983 TRACE("copy %s to %s style 0x%x\n",debugstr_w(source),debugstr_w(target),style);
984
985 /* before copy processing */
986 if (style & SP_COPY_REPLACEONLY)
987 {
988 if (GetFileAttributesW(target) == INVALID_FILE_ATTRIBUTES)
989 docopy = FALSE;
990 }
991 if (style & (SP_COPY_NEWER_OR_SAME | SP_COPY_NEWER_ONLY | SP_COPY_FORCE_NEWER))
992 {
993 DWORD VersionSizeSource=0;
994 DWORD VersionSizeTarget=0;
995 DWORD zero=0;
996
997 /*
998 * This is sort of an interesting workaround. You see, calling
999 * GetVersionInfoSize on a builtin dll loads that dll into memory
1000 * and we do not properly unload builtin dlls.. so we effectively
1001 * lock into memory all the targets we are replacing. This leads
1002 * to problems when we try to register the replaced dlls.
1003 *
1004 * So I will test for the existence of the files first so that
1005 * we just basically unconditionally replace the builtin versions.
1006 */
1007 if ((GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) &&
1008 (GetFileAttributesW(source) != INVALID_FILE_ATTRIBUTES))
1009 {
1010 VersionSizeSource = GetFileVersionInfoSizeW((LPWSTR)source,&zero);
1011 VersionSizeTarget = GetFileVersionInfoSizeW((LPWSTR)target,&zero);
1012 }
1013
1014 TRACE("SizeTarget %i ... SizeSource %i\n",VersionSizeTarget,
1015 VersionSizeSource);
1016
1017 if (VersionSizeSource && VersionSizeTarget)
1018 {
1019 LPVOID VersionSource;
1020 LPVOID VersionTarget;
1021 VS_FIXEDFILEINFO *TargetInfo;
1022 VS_FIXEDFILEINFO *SourceInfo;
1023 UINT length;
1024 WCHAR SubBlock[2]={'\\',0};
1025 DWORD ret;
1026
1027 VersionSource = HeapAlloc(GetProcessHeap(),0,VersionSizeSource);
1028 VersionTarget = HeapAlloc(GetProcessHeap(),0,VersionSizeTarget);
1029
1030 ret = GetFileVersionInfoW((LPWSTR)source,0,VersionSizeSource,VersionSource);
1031 if (ret)
1032 ret = GetFileVersionInfoW((LPWSTR)target, 0, VersionSizeTarget,
1033 VersionTarget);
1034
1035 if (ret)
1036 {
1037 ret = VerQueryValueW(VersionSource, SubBlock,
1038 (LPVOID*)&SourceInfo, &length);
1039 if (ret)
1040 ret = VerQueryValueW(VersionTarget, SubBlock,
1041 (LPVOID*)&TargetInfo, &length);
1042
1043 if (ret)
1044 {
1045 FILEPATHS_W filepaths;
1046
1047 TRACE("Versions: Source %i.%i target %i.%i\n",
1048 SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS,
1049 TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS);
1050
1051 /* used in case of notification */
1052 filepaths.Target = target;
1053 filepaths.Source = source;
1054 filepaths.Win32Error = 0;
1055 filepaths.Flags = 0;
1056
1057 if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS)
1058 {
1059 if (handler)
1060 docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1061 else
1062 docopy = FALSE;
1063 }
1064 else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS)
1065 && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS))
1066 {
1067 if (handler)
1068 docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1069 else
1070 docopy = FALSE;
1071 }
1072 else if ((style & SP_COPY_NEWER_ONLY) &&
1073 (TargetInfo->dwFileVersionMS ==
1074 SourceInfo->dwFileVersionMS)
1075 &&(TargetInfo->dwFileVersionLS ==
1076 SourceInfo->dwFileVersionLS))
1077 {
1078 if (handler)
1079 docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1080 else
1081 docopy = FALSE;
1082 }
1083 }
1084 }
1085 HeapFree(GetProcessHeap(),0,VersionSource);
1086 HeapFree(GetProcessHeap(),0,VersionTarget);
1087 }
1088 }
1089 if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE))
1090 {
1091 if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES)
1092 {
1093 FIXME("Notify user target file exists\n");
1094 docopy = FALSE;
1095 }
1096 }
1097 if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE |
1098 SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP))
1099 {
1100 ERR("Unsupported style(s) 0x%x\n",style);
1101 }
1102
1103 if (docopy)
1104 {
1105 rc = CopyFileW(source,target,FALSE);
1106 TRACE("Did copy... rc was %i\n",rc);
1107 }
1108
1109 /* after copy processing */
1110 if (style & SP_COPY_DELETESOURCE)
1111 {
1112 if (rc)
1113 DeleteFileW(source);
1114 }
1115
1116 return rc;
1117 }
1118
1119 /***********************************************************************
1120 * SetupInstallFileA (SETUPAPI.@)
1121 */
1122 BOOL WINAPI SetupInstallFileA( HINF hinf, PINFCONTEXT inf_context, PCSTR source, PCSTR root,
1123 PCSTR dest, DWORD style, PSP_FILE_CALLBACK_A handler, PVOID context )
1124 {
1125 BOOL ret = FALSE;
1126 struct callback_WtoA_context ctx;
1127 UNICODE_STRING sourceW, rootW, destW;
1128
1129 TRACE("%p %p %s %s %s %x %p %p\n", hinf, inf_context, debugstr_a(source), debugstr_a(root),
1130 debugstr_a(dest), style, handler, context);
1131
1132 sourceW.Buffer = rootW.Buffer = destW.Buffer = NULL;
1133 if (source && !RtlCreateUnicodeStringFromAsciiz( &sourceW, source ))
1134 {
1135 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1136 return FALSE;
1137 }
1138 if (root && !RtlCreateUnicodeStringFromAsciiz( &rootW, root ))
1139 {
1140 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1141 goto exit;
1142 }
1143 if (dest && !RtlCreateUnicodeStringFromAsciiz( &destW, dest ))
1144 {
1145 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1146 goto exit;
1147 }
1148
1149 ctx.orig_context = context;
1150 ctx.orig_handler = handler;
1151
1152 ret = SetupInstallFileW( hinf, inf_context, sourceW.Buffer, rootW.Buffer, destW.Buffer, style, QUEUE_callback_WtoA, &ctx );
1153
1154 exit:
1155 RtlFreeUnicodeString( &sourceW );
1156 RtlFreeUnicodeString( &rootW );
1157 RtlFreeUnicodeString( &destW );
1158 return ret;
1159 }
1160
1161 /***********************************************************************
1162 * SetupInstallFileW (SETUPAPI.@)
1163 */
1164 BOOL WINAPI SetupInstallFileW( HINF hinf, PINFCONTEXT inf_context, PCWSTR source, PCWSTR root,
1165 PCWSTR dest, DWORD style, PSP_FILE_CALLBACK_W handler, PVOID context )
1166 {
1167 static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0};
1168
1169 BOOL ret, absolute = (root && *root && !(style & SP_COPY_SOURCE_ABSOLUTE));
1170 WCHAR *buffer, *p, *inf_source = NULL;
1171 unsigned int len;
1172
1173 TRACE("%p %p %s %s %s %x %p %p\n", hinf, inf_context, debugstr_w(source), debugstr_w(root),
1174 debugstr_w(dest), style, handler, context);
1175
1176 if (hinf)
1177 {
1178 INFCONTEXT ctx;
1179
1180 if (!inf_context)
1181 {
1182 inf_context = &ctx;
1183 if (!SetupFindFirstLineW( hinf, CopyFiles, NULL, inf_context )) return FALSE;
1184 }
1185 if (!SetupGetStringFieldW( inf_context, 1, NULL, 0, (PDWORD) &len )) return FALSE;
1186 if (!(inf_source = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1187 {
1188 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1189 return FALSE;
1190 }
1191 if (!SetupGetStringFieldW( inf_context, 1, inf_source, len, NULL )) return FALSE;
1192 source = inf_source;
1193 }
1194 else if (!source)
1195 {
1196 SetLastError( ERROR_INVALID_PARAMETER );
1197 return FALSE;
1198 }
1199
1200 len = strlenW( source ) + 1;
1201 if (absolute) len += strlenW( root ) + 1;
1202
1203 if (!(p = buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1204 {
1205 HeapFree( GetProcessHeap(), 0, inf_source );
1206 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1207 return FALSE;
1208 }
1209
1210 if (absolute)
1211 {
1212 strcpyW( buffer, root );
1213 p += strlenW( buffer );
1214 if (p[-1] != '\\') *p++ = '\\';
1215 }
1216 while (*source == '\\') source++;
1217 strcpyW( p, source );
1218
1219 ret = do_file_copyW( buffer, dest, style, handler, context );
1220
1221 HeapFree( GetProcessHeap(), 0, inf_source );
1222 HeapFree( GetProcessHeap(), 0, buffer );
1223 return ret;
1224 }
1225
1226 /***********************************************************************
1227 * SetupCommitFileQueueW (SETUPAPI.@)
1228 */
1229 BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler,
1230 PVOID context )
1231 {
1232 struct file_queue *queue = handle;
1233 struct file_op *op;
1234 BOOL result = FALSE;
1235 FILEPATHS_W paths;
1236 UINT op_result;
1237
1238 paths.Source = paths.Target = NULL;
1239
1240 if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count)
1241 return TRUE; /* nothing to do */
1242
1243 if (!handler( context, SPFILENOTIFY_STARTQUEUE, (UINT_PTR)owner, 0 )) return FALSE;
1244
1245 /* perform deletes */
1246
1247 if (queue->delete_queue.count)
1248 {
1249 if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE,
1250 queue->delete_queue.count ))) goto done;
1251 for (op = queue->delete_queue.head; op; op = op->next)
1252 {
1253 build_filepathsW( op, &paths );
1254 op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE);
1255 if (op_result == FILEOP_ABORT) goto done;
1256 while (op_result == FILEOP_DOIT)
1257 {
1258 TRACE( "deleting file %s\n", debugstr_w(paths.Target) );
1259 if (DeleteFileW( paths.Target )) break; /* success */
1260 paths.Win32Error = GetLastError();
1261 op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 );
1262 if (op_result == FILEOP_ABORT) goto done;
1263 }
1264 handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 );
1265 }
1266 handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 );
1267 }
1268
1269 /* perform renames */
1270
1271 if (queue->rename_queue.count)
1272 {
1273 if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME,
1274 queue->rename_queue.count ))) goto done;
1275 for (op = queue->rename_queue.head; op; op = op->next)
1276 {
1277 build_filepathsW( op, &paths );
1278 op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME);
1279 if (op_result == FILEOP_ABORT) goto done;
1280 while (op_result == FILEOP_DOIT)
1281 {
1282 TRACE( "renaming file %s -> %s\n",
1283 debugstr_w(paths.Source), debugstr_w(paths.Target) );
1284 if (MoveFileW( paths.Source, paths.Target )) break; /* success */
1285 paths.Win32Error = GetLastError();
1286 op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 );
1287 if (op_result == FILEOP_ABORT) goto done;
1288 }
1289 handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 );
1290 }
1291 handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 );
1292 }
1293
1294 /* perform copies */
1295
1296 if (queue->copy_queue.count)
1297 {
1298 if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY,
1299 queue->copy_queue.count ))) goto done;
1300 for (op = queue->copy_queue.head; op; op = op->next)
1301 {
1302 WCHAR newpath[MAX_PATH];
1303
1304 build_filepathsW( op, &paths );
1305 op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY );
1306 if (op_result == FILEOP_ABORT) goto done;
1307 if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT;
1308 while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH)
1309 {
1310 TRACE( "copying file %s -> %s\n",
1311 debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ),
1312 debugstr_w(paths.Target) );
1313 if (op->dst_path)
1314 {
1315 if (!create_full_pathW( op->dst_path ))
1316 {
1317 paths.Win32Error = GetLastError();
1318 op_result = handler( context, SPFILENOTIFY_COPYERROR,
1319 (UINT_PTR)&paths, (UINT_PTR)newpath );
1320 if (op_result == FILEOP_ABORT) goto done;
1321 }
1322 }
1323 if (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source,
1324 paths.Target, op->style, handler, context )) break; /* success */
1325 /* try to extract it from the cabinet file */
1326 if (op->src_tag)
1327 {
1328 if (extract_cabinet_file( op->src_tag, op->src_root,
1329 paths.Source, paths.Target )) break;
1330 }
1331 paths.Win32Error = GetLastError();
1332 op_result = handler( context, SPFILENOTIFY_COPYERROR,
1333 (UINT_PTR)&paths, (UINT_PTR)newpath );
1334 if (op_result == FILEOP_ABORT) goto done;
1335 }
1336 if (op->dst_sd)
1337 {
1338 PSID psidOwner = NULL, psidGroup = NULL;
1339 PACL pDacl = NULL, pSacl = NULL;
1340 SECURITY_INFORMATION security_info = 0;
1341 BOOL present, dummy;
1342
1343 if (GetSecurityDescriptorOwner( op->dst_sd, &psidOwner, &dummy ) && psidOwner)
1344 security_info |= OWNER_SECURITY_INFORMATION;
1345 if (GetSecurityDescriptorGroup( op->dst_sd, &psidGroup, &dummy ) && psidGroup)
1346 security_info |= GROUP_SECURITY_INFORMATION;
1347 if (GetSecurityDescriptorDacl( op->dst_sd, &present, &pDacl, &dummy ))
1348 security_info |= DACL_SECURITY_INFORMATION;
1349 if (GetSecurityDescriptorSacl( op->dst_sd, &present, &pSacl, &dummy ))
1350 security_info |= DACL_SECURITY_INFORMATION;
1351 SetNamedSecurityInfoW( (LPWSTR)paths.Target, SE_FILE_OBJECT, security_info,
1352 psidOwner, psidGroup, pDacl, pSacl );
1353 /* Yes, ignore the return code... */
1354 }
1355 handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 );
1356 }
1357 handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 );
1358 }
1359
1360
1361 result = TRUE;
1362
1363 done:
1364 handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 );
1365 HeapFree( GetProcessHeap(), 0, (void *)paths.Source );
1366 HeapFree( GetProcessHeap(), 0, (void *)paths.Target );
1367 return result;
1368 }
1369
1370
1371 /***********************************************************************
1372 * SetupScanFileQueueA (SETUPAPI.@)
1373 */
1374 BOOL WINAPI SetupScanFileQueueA( HSPFILEQ handle, DWORD flags, HWND window,
1375 PSP_FILE_CALLBACK_A handler, PVOID context, PDWORD result )
1376 {
1377 struct callback_WtoA_context ctx;
1378
1379 TRACE("%p %x %p %p %p %p\n", handle, flags, window, handler, context, result);
1380
1381 ctx.orig_context = context;
1382 ctx.orig_handler = handler;
1383
1384 return SetupScanFileQueueW( handle, flags, window, QUEUE_callback_WtoA, &ctx, result );
1385 }
1386
1387
1388 /***********************************************************************
1389 * SetupScanFileQueueW (SETUPAPI.@)
1390 */
1391 BOOL WINAPI SetupScanFileQueueW( HSPFILEQ handle, DWORD flags, HWND window,
1392 PSP_FILE_CALLBACK_W handler, PVOID context, PDWORD result )
1393 {
1394 struct file_queue *queue = handle;
1395 struct file_op *op;
1396 FILEPATHS_W paths;
1397 UINT notification = 0;
1398 BOOL ret = FALSE;
1399
1400 TRACE("%p %x %p %p %p %p\n", handle, flags, window, handler, context, result);
1401
1402 *result = FALSE;
1403
1404 if (!queue->copy_queue.count) return TRUE;
1405
1406 if (flags & SPQ_SCAN_USE_CALLBACK) notification = SPFILENOTIFY_QUEUESCAN;
1407 else if (flags & SPQ_SCAN_USE_CALLBACKEX) notification = SPFILENOTIFY_QUEUESCAN_EX;
1408
1409 if (flags & ~(SPQ_SCAN_USE_CALLBACK | SPQ_SCAN_USE_CALLBACKEX))
1410 {
1411 FIXME("flags %x not fully implemented\n", flags);
1412 }
1413
1414 paths.Source = paths.Target = NULL;
1415
1416 for (op = queue->copy_queue.head; op; op = op->next)
1417 {
1418 build_filepathsW( op, &paths );
1419 switch (notification)
1420 {
1421 case SPFILENOTIFY_QUEUESCAN:
1422 /* FIXME: handle delay flag */
1423 if (handler( context, notification, (UINT_PTR)paths.Target, 0 )) goto done;
1424 break;
1425 case SPFILENOTIFY_QUEUESCAN_EX:
1426 if (handler( context, notification, (UINT_PTR)&paths, 0 )) goto done;
1427 break;
1428 default:
1429 ret = TRUE; goto done;
1430 }
1431 }
1432
1433 *result = TRUE;
1434
1435 done:
1436 HeapFree( GetProcessHeap(), 0, (void *)paths.Source );
1437 HeapFree( GetProcessHeap(), 0, (void *)paths.Target );
1438 return ret;
1439 }
1440
1441
1442 /***********************************************************************
1443 * SetupGetFileQueueCount (SETUPAPI.@)
1444 */
1445 BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result )
1446 {
1447 struct file_queue *queue = handle;
1448
1449 switch(op)
1450 {
1451 case FILEOP_COPY:
1452 *result = queue->copy_queue.count;
1453 return TRUE;
1454 case FILEOP_RENAME:
1455 *result = queue->rename_queue.count;
1456 return TRUE;
1457 case FILEOP_DELETE:
1458 *result = queue->delete_queue.count;
1459 return TRUE;
1460 }
1461 return FALSE;
1462 }
1463
1464
1465 /***********************************************************************
1466 * SetupGetFileQueueFlags (SETUPAPI.@)
1467 */
1468 BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags )
1469 {
1470 struct file_queue *queue = handle;
1471 *flags = queue->flags;
1472 return TRUE;
1473 }
1474
1475
1476 /***********************************************************************
1477 * SetupSetFileQueueFlags (SETUPAPI.@)
1478 */
1479 BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags )
1480 {
1481 struct file_queue *queue = handle;
1482 queue->flags = (queue->flags & ~mask) | flags;
1483 return TRUE;
1484 }
1485
1486
1487 /***********************************************************************
1488 * SetupSetFileQueueAlternatePlatformA (SETUPAPI.@)
1489 */
1490 BOOL WINAPI SetupSetFileQueueAlternatePlatformA(HSPFILEQ handle, PSP_ALTPLATFORM_INFO platform, PCSTR catalogfile)
1491 {
1492 FIXME("(%p, %p, %s) stub!\n", handle, platform, debugstr_a(catalogfile));
1493 return FALSE;
1494 }
1495
1496
1497 /***********************************************************************
1498 * SetupSetFileQueueAlternatePlatformW (SETUPAPI.@)
1499 */
1500 BOOL WINAPI SetupSetFileQueueAlternatePlatformW(HSPFILEQ handle, PSP_ALTPLATFORM_INFO platform, PCWSTR catalogfile)
1501 {
1502 FIXME("(%p, %p, %s) stub!\n", handle, platform, debugstr_w(catalogfile));
1503 return FALSE;
1504 }
1505
1506
1507 /***********************************************************************
1508 * SetupInitDefaultQueueCallback (SETUPAPI.@)
1509 */
1510 PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner )
1511 {
1512 return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL );
1513 }
1514
1515
1516 /***********************************************************************
1517 * SetupInitDefaultQueueCallbackEx (SETUPAPI.@)
1518 */
1519 PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg,
1520 DWORD reserved1, PVOID reserved2 )
1521 {
1522 struct default_callback_context *context;
1523
1524 if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) )))
1525 {
1526 context->owner = owner;
1527 context->progress = progress;
1528 context->message = msg;
1529 }
1530 return context;
1531 }
1532
1533
1534 /***********************************************************************
1535 * SetupTermDefaultQueueCallback (SETUPAPI.@)
1536 */
1537 void WINAPI SetupTermDefaultQueueCallback( PVOID context )
1538 {
1539 HeapFree( GetProcessHeap(), 0, context );
1540 }
1541
1542
1543 /***********************************************************************
1544 * SetupDefaultQueueCallbackA (SETUPAPI.@)
1545 */
1546 UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification,
1547 UINT_PTR param1, UINT_PTR param2 )
1548 {
1549 FILEPATHS_A *paths = (FILEPATHS_A *)param1;
1550 struct default_callback_context *ctx = (struct default_callback_context *)context;
1551
1552 switch(notification)
1553 {
1554 case SPFILENOTIFY_STARTQUEUE:
1555 TRACE( "start queue\n" );
1556 return TRUE;
1557 case SPFILENOTIFY_ENDQUEUE:
1558 TRACE( "end queue\n" );
1559 return 0;
1560 case SPFILENOTIFY_STARTSUBQUEUE:
1561 TRACE( "start subqueue %ld count %ld\n", param1, param2 );
1562 return TRUE;
1563 case SPFILENOTIFY_ENDSUBQUEUE:
1564 TRACE( "end subqueue %ld\n", param1 );
1565 return 0;
1566 case SPFILENOTIFY_STARTDELETE:
1567 TRACE( "start delete %s\n", debugstr_a(paths->Target) );
1568 return FILEOP_DOIT;
1569 case SPFILENOTIFY_ENDDELETE:
1570 TRACE( "end delete %s\n", debugstr_a(paths->Target) );
1571 return 0;
1572 case SPFILENOTIFY_DELETEERROR:
1573 /*Windows Ignores attempts to delete files / folders which do not exist*/
1574 if ((paths->Win32Error != ERROR_FILE_NOT_FOUND) && (paths->Win32Error != ERROR_PATH_NOT_FOUND))
1575 SetupDeleteErrorA(ctx->owner, NULL, paths->Target, paths->Win32Error, 0);
1576 return FILEOP_SKIP;
1577 case SPFILENOTIFY_STARTRENAME:
1578 TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1579 return FILEOP_DOIT;
1580 case SPFILENOTIFY_ENDRENAME:
1581 TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1582 return 0;
1583 case SPFILENOTIFY_RENAMEERROR:
1584 SetupRenameErrorA(ctx->owner, NULL, paths->Source, paths->Target, paths->Win32Error, 0);
1585 return FILEOP_SKIP;
1586 case SPFILENOTIFY_STARTCOPY:
1587 TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1588 return FILEOP_DOIT;
1589 case SPFILENOTIFY_ENDCOPY:
1590 TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1591 return 0;
1592 case SPFILENOTIFY_COPYERROR:
1593 ERR( "copy error %d %s -> %s\n", paths->Win32Error,
1594 debugstr_a(paths->Source), debugstr_a(paths->Target) );
1595 return FILEOP_SKIP;
1596 case SPFILENOTIFY_NEEDMEDIA:
1597 TRACE( "need media\n" );
1598 return FILEOP_SKIP;
1599 default:
1600 FIXME( "notification %d params %lx,%lx\n", notification, param1, param2 );
1601 break;
1602 }
1603 return 0;
1604 }
1605
1606
1607 /***********************************************************************
1608 * SetupDefaultQueueCallbackW (SETUPAPI.@)
1609 */
1610 UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification,
1611 UINT_PTR param1, UINT_PTR param2 )
1612 {
1613 FILEPATHS_W *paths = (FILEPATHS_W *)param1;
1614 struct default_callback_context *ctx = (struct default_callback_context *)context;
1615
1616 switch(notification)
1617 {
1618 case SPFILENOTIFY_STARTQUEUE:
1619 TRACE( "start queue\n" );
1620 return TRUE;
1621 case SPFILENOTIFY_ENDQUEUE:
1622 TRACE( "end queue\n" );
1623 return 0;
1624 case SPFILENOTIFY_STARTSUBQUEUE:
1625 TRACE( "start subqueue %ld count %ld\n", param1, param2 );
1626 return TRUE;
1627 case SPFILENOTIFY_ENDSUBQUEUE:
1628 TRACE( "end subqueue %ld\n", param1 );
1629 return 0;
1630 case SPFILENOTIFY_STARTDELETE:
1631 TRACE( "start delete %s\n", debugstr_w(paths->Target) );
1632 return FILEOP_DOIT;
1633 case SPFILENOTIFY_ENDDELETE:
1634 TRACE( "end delete %s\n", debugstr_w(paths->Target) );
1635 return 0;
1636 case SPFILENOTIFY_DELETEERROR:
1637 /*Windows Ignores attempts to delete files / folders which do not exist*/
1638 if ((paths->Win32Error != ERROR_FILE_NOT_FOUND) && (paths->Win32Error != ERROR_PATH_NOT_FOUND))
1639 SetupDeleteErrorW(ctx->owner, NULL, paths->Target, paths->Win32Error, 0);
1640 return FILEOP_SKIP;
1641 case SPFILENOTIFY_STARTRENAME:
1642 SetupRenameErrorW(ctx->owner, NULL, paths->Source, paths->Target, paths->Win32Error, 0);
1643 return FILEOP_DOIT;
1644 case SPFILENOTIFY_ENDRENAME:
1645 TRACE( "end rename %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1646 return 0;
1647 case SPFILENOTIFY_RENAMEERROR:
1648 ERR( "rename error %d %s -> %s\n", paths->Win32Error,
1649 debugstr_w(paths->Source), debugstr_w(paths->Target) );
1650 return FILEOP_SKIP;
1651 case SPFILENOTIFY_STARTCOPY:
1652 TRACE( "start copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1653 return FILEOP_DOIT;
1654 case SPFILENOTIFY_ENDCOPY:
1655 TRACE( "end copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1656 return 0;
1657 case SPFILENOTIFY_COPYERROR:
1658 ERR( "copy error %d %s -> %s\n", paths->Win32Error,
1659 debugstr_w(paths->Source), debugstr_w(paths->Target) );
1660 return FILEOP_SKIP;
1661 case SPFILENOTIFY_NEEDMEDIA:
1662 TRACE( "need media\n" );
1663 return FILEOP_SKIP;
1664 default:
1665 FIXME( "notification %d params %lx,%lx\n", notification, param1, param2 );
1666 break;
1667 }
1668 return 0;
1669 }
1670
1671 /***********************************************************************
1672 * SetupDeleteErrorA (SETUPAPI.@)
1673 */
1674
1675 UINT WINAPI SetupDeleteErrorA( HWND parent, PCSTR dialogTitle, PCSTR file,
1676 UINT w32error, DWORD style)
1677 {
1678 FIXME( "stub: (Error Number %d when attempting to delete %s)\n",
1679 w32error, debugstr_a(file) );
1680 return DPROMPT_SKIPFILE;
1681 }
1682
1683 /***********************************************************************
1684 * SetupDeleteErrorW (SETUPAPI.@)
1685 */
1686
1687 UINT WINAPI SetupDeleteErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR file,
1688 UINT w32error, DWORD style)
1689 {
1690 FIXME( "stub: (Error Number %d when attempting to delete %s)\n",
1691 w32error, debugstr_w(file) );
1692 return DPROMPT_SKIPFILE;
1693 }
1694
1695 /***********************************************************************
1696 * SetupRenameErrorA (SETUPAPI.@)
1697 */
1698
1699 UINT WINAPI SetupRenameErrorA( HWND parent, PCSTR dialogTitle, PCSTR source,
1700 PCSTR target, UINT w32error, DWORD style)
1701 {
1702 FIXME( "stub: (Error Number %d when attempting to rename %s to %s)\n",
1703 w32error, debugstr_a(source), debugstr_a(target));
1704 return DPROMPT_SKIPFILE;
1705 }
1706
1707 /***********************************************************************
1708 * SetupRenameErrorW (SETUPAPI.@)
1709 */
1710
1711 UINT WINAPI SetupRenameErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR source,
1712 PCWSTR target, UINT w32error, DWORD style)
1713 {
1714 FIXME( "stub: (Error Number %d when attempting to rename %s to %s)\n",
1715 w32error, debugstr_w(source), debugstr_w(target));
1716 return DPROMPT_SKIPFILE;
1717 }
1718
1719
1720 /***********************************************************************
1721 * SetupCopyErrorA (SETUPAPI.@)
1722 */
1723
1724 UINT WINAPI SetupCopyErrorA( HWND parent, PCSTR dialogTitle, PCSTR diskname,
1725 PCSTR sourcepath, PCSTR sourcefile, PCSTR targetpath,
1726 UINT w32error, DWORD style, PSTR pathbuffer,
1727 DWORD buffersize, PDWORD requiredsize)
1728 {
1729 FIXME( "stub: (Error Number %d when attempting to copy file %s from %s to %s)\n",
1730 w32error, debugstr_a(sourcefile), debugstr_a(sourcepath) ,debugstr_a(targetpath));
1731 return DPROMPT_SKIPFILE;
1732 }
1733
1734 /***********************************************************************
1735 * SetupCopyErrorW (SETUPAPI.@)
1736 */
1737
1738 UINT WINAPI SetupCopyErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR diskname,
1739 PCWSTR sourcepath, PCWSTR sourcefile, PCWSTR targetpath,
1740 UINT w32error, DWORD style, PWSTR pathbuffer,
1741 DWORD buffersize, PDWORD requiredsize)
1742 {
1743 FIXME( "stub: (Error Number %d when attempting to copy file %s from %s to %s)\n",
1744 w32error, debugstr_w(sourcefile), debugstr_w(sourcepath) ,debugstr_w(targetpath));
1745 return DPROMPT_SKIPFILE;
1746 }
1747
1748 /***********************************************************************
1749 * pSetupGetQueueFlags (SETUPAPI.@)
1750 */
1751 DWORD WINAPI pSetupGetQueueFlags( HSPFILEQ handle )
1752 {
1753 struct file_queue *queue = handle;
1754 return queue->flags;
1755 }
1756
1757 /***********************************************************************
1758 * pSetupSetQueueFlags (SETUPAPI.@)
1759 */
1760 BOOL WINAPI pSetupSetQueueFlags( HSPFILEQ handle, DWORD flags )
1761 {
1762 struct file_queue *queue = handle;
1763 queue->flags = flags;
1764 return TRUE;
1765 }