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