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