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