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