xref: /reactos/dll/win32/setupapi/queue.c (revision 5100859e)
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 #endif
1071 
1072     TRACE("copy %s to %s style 0x%x\n",debugstr_w(source),debugstr_w(target),style);
1073 
1074 #ifdef __REACTOS__
1075     /* Get a temp file name */
1076     if (!GetTempPathW(ARRAYSIZE(TempPath), TempPath))
1077     {
1078         ERR("GetTempPathW error\n");
1079         return FALSE;
1080     }
1081     if (!GetTempFileNameW(TempPath, L"", 0, TempFile))
1082     {
1083         ERR("GetTempFileNameW(%s) error\n", debugstr_w(TempPath));
1084         return FALSE;
1085     }
1086 
1087     /* Try to open the source file */
1088     hSource = LZOpenFileW((LPWSTR)source, &OfStruct, OF_READ);
1089     if (hSource < 0)
1090     {
1091         ERR("LZOpenFileW(1) error %d %s\n", (int)hSource, debugstr_w(source));
1092         return FALSE;
1093     }
1094 
1095     /* Extract the compressed file to a temp location */
1096     hTemp = LZOpenFileW(TempFile, &OfStruct, OF_CREATE);
1097     if (hTemp < 0)
1098     {
1099         DWORD dwLastError = GetLastError();
1100 
1101         ERR("LZOpenFileW(2) error %d %s\n", (int)hTemp, debugstr_w(TempFile));
1102 
1103         /* Close the source handle */
1104         LZClose(hSource);
1105 
1106         /* Restore error condition triggered by LZOpenFileW */
1107         SetLastError(dwLastError);
1108         return FALSE;
1109     }
1110 
1111     LZCopy(hSource, hTemp);
1112     LZClose(hSource);
1113     LZClose(hTemp);
1114 #endif
1115 
1116     /* before copy processing */
1117     if (style & SP_COPY_REPLACEONLY)
1118     {
1119         if (GetFileAttributesW(target) == INVALID_FILE_ATTRIBUTES)
1120             docopy = FALSE;
1121     }
1122     if (style & (SP_COPY_NEWER_OR_SAME | SP_COPY_NEWER_ONLY | SP_COPY_FORCE_NEWER))
1123     {
1124         DWORD VersionSizeSource=0;
1125         DWORD VersionSizeTarget=0;
1126         DWORD zero=0;
1127 
1128         /*
1129          * This is sort of an interesting workaround. You see, calling
1130          * GetVersionInfoSize on a builtin dll loads that dll into memory
1131          * and we do not properly unload builtin dlls.. so we effectively
1132          * lock into memory all the targets we are replacing. This leads
1133          * to problems when we try to register the replaced dlls.
1134          *
1135          * So I will test for the existence of the files first so that
1136          * we just basically unconditionally replace the builtin versions.
1137          */
1138         if ((GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) &&
1139             (GetFileAttributesW(TempFile) != INVALID_FILE_ATTRIBUTES))
1140         {
1141             VersionSizeSource = GetFileVersionInfoSizeW(TempFile,&zero);
1142             VersionSizeTarget = GetFileVersionInfoSizeW((LPWSTR)target,&zero);
1143         }
1144 
1145         TRACE("SizeTarget %i ... SizeSource %i\n",VersionSizeTarget,
1146                 VersionSizeSource);
1147 
1148         if (VersionSizeSource && VersionSizeTarget)
1149         {
1150             LPVOID VersionSource;
1151             LPVOID VersionTarget;
1152             VS_FIXEDFILEINFO *TargetInfo;
1153             VS_FIXEDFILEINFO *SourceInfo;
1154             UINT length;
1155             WCHAR  SubBlock[2]={'\\',0};
1156             DWORD  ret;
1157 
1158             VersionSource = HeapAlloc(GetProcessHeap(),0,VersionSizeSource);
1159             VersionTarget = HeapAlloc(GetProcessHeap(),0,VersionSizeTarget);
1160 
1161             ret = GetFileVersionInfoW(TempFile,0,VersionSizeSource,VersionSource);
1162             if (ret)
1163               ret = GetFileVersionInfoW((LPWSTR)target, 0, VersionSizeTarget,
1164                     VersionTarget);
1165 
1166             if (ret)
1167             {
1168                 ret = VerQueryValueW(VersionSource, SubBlock,
1169                                     (LPVOID*)&SourceInfo, &length);
1170                 if (ret)
1171                     ret = VerQueryValueW(VersionTarget, SubBlock,
1172                                          (LPVOID*)&TargetInfo, &length);
1173 
1174                 if (ret)
1175                 {
1176                     FILEPATHS_W filepaths;
1177 
1178                     TRACE("Versions: Source %i.%i target %i.%i\n",
1179                       SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS,
1180                       TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS);
1181 
1182                     /* used in case of notification */
1183                     filepaths.Target = target;
1184                     filepaths.Source = source;
1185                     filepaths.Win32Error = 0;
1186                     filepaths.Flags = 0;
1187 
1188                     if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS)
1189                     {
1190                         if (handler)
1191                             docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1192                         else
1193                             docopy = FALSE;
1194                     }
1195                     else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS)
1196                              && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS))
1197                     {
1198                         if (handler)
1199                             docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1200                         else
1201                             docopy = FALSE;
1202                     }
1203                     else if ((style & SP_COPY_NEWER_ONLY) &&
1204                         (TargetInfo->dwFileVersionMS ==
1205                          SourceInfo->dwFileVersionMS)
1206                         &&(TargetInfo->dwFileVersionLS ==
1207                         SourceInfo->dwFileVersionLS))
1208                     {
1209                         if (handler)
1210                             docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1211                         else
1212                             docopy = FALSE;
1213                     }
1214                 }
1215             }
1216             HeapFree(GetProcessHeap(),0,VersionSource);
1217             HeapFree(GetProcessHeap(),0,VersionTarget);
1218         }
1219     }
1220     if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE))
1221     {
1222         if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES)
1223         {
1224             FIXME("Notify user target file exists\n");
1225             docopy = FALSE;
1226         }
1227     }
1228     if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE |
1229                  SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP))
1230     {
1231         ERR("Unsupported style(s) 0x%x\n",style);
1232     }
1233 
1234     if (docopy)
1235     {
1236         rc = MoveFileExW(TempFile,target,MOVEFILE_REPLACE_EXISTING);
1237         TRACE("Did copy... rc was %i\n",rc);
1238     }
1239 
1240     /* after copy processing */
1241     if (style & SP_COPY_DELETESOURCE)
1242     {
1243        if (rc)
1244             DeleteFileW(source);
1245     }
1246 
1247     return rc;
1248 }
1249 
1250 /***********************************************************************
1251  *            SetupInstallFileA   (SETUPAPI.@)
1252  */
1253 BOOL WINAPI SetupInstallFileA( HINF hinf, PINFCONTEXT inf_context, PCSTR source, PCSTR root,
1254                                PCSTR dest, DWORD style, PSP_FILE_CALLBACK_A handler, PVOID context )
1255 {
1256     BOOL ret = FALSE;
1257     struct callback_WtoA_context ctx;
1258     UNICODE_STRING sourceW, rootW, destW;
1259 
1260     TRACE("%p %p %s %s %s %x %p %p\n", hinf, inf_context, debugstr_a(source), debugstr_a(root),
1261           debugstr_a(dest), style, handler, context);
1262 
1263     sourceW.Buffer = rootW.Buffer = destW.Buffer = NULL;
1264     if (source && !RtlCreateUnicodeStringFromAsciiz( &sourceW, source ))
1265     {
1266         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1267         return FALSE;
1268     }
1269     if (root && !RtlCreateUnicodeStringFromAsciiz( &rootW, root ))
1270     {
1271         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1272         goto exit;
1273     }
1274     if (dest && !RtlCreateUnicodeStringFromAsciiz( &destW, dest ))
1275     {
1276         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1277         goto exit;
1278     }
1279 
1280     ctx.orig_context = context;
1281     ctx.orig_handler = handler;
1282 
1283     ret = SetupInstallFileW( hinf, inf_context, sourceW.Buffer, rootW.Buffer, destW.Buffer, style, QUEUE_callback_WtoA, &ctx );
1284 
1285 exit:
1286     RtlFreeUnicodeString( &sourceW );
1287     RtlFreeUnicodeString( &rootW );
1288     RtlFreeUnicodeString( &destW );
1289     return ret;
1290 }
1291 
1292 /***********************************************************************
1293  *            SetupInstallFileW   (SETUPAPI.@)
1294  */
1295 BOOL WINAPI SetupInstallFileW( HINF hinf, PINFCONTEXT inf_context, PCWSTR source, PCWSTR root,
1296                                PCWSTR dest, DWORD style, PSP_FILE_CALLBACK_W handler, PVOID context )
1297 {
1298     static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0};
1299 
1300     BOOL ret, absolute = (root && *root && !(style & SP_COPY_SOURCE_ABSOLUTE));
1301     WCHAR *buffer, *p, *inf_source = NULL;
1302     unsigned int len;
1303 
1304     TRACE("%p %p %s %s %s %x %p %p\n", hinf, inf_context, debugstr_w(source), debugstr_w(root),
1305           debugstr_w(dest), style, handler, context);
1306 
1307     if (hinf)
1308     {
1309         INFCONTEXT ctx;
1310 
1311         if (!inf_context)
1312         {
1313             inf_context = &ctx;
1314             if (!SetupFindFirstLineW( hinf, CopyFiles, NULL, inf_context )) return FALSE;
1315         }
1316         if (!SetupGetStringFieldW( inf_context, 1, NULL, 0, (PDWORD) &len )) return FALSE;
1317         if (!(inf_source = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1318         {
1319             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1320             return FALSE;
1321         }
1322         if (!SetupGetStringFieldW( inf_context, 1, inf_source, len, NULL ))
1323         {
1324             HeapFree( GetProcessHeap(), 0, inf_source );
1325             return FALSE;
1326         }
1327         source = inf_source;
1328     }
1329     else if (!source)
1330     {
1331         SetLastError( ERROR_INVALID_PARAMETER );
1332         return FALSE;
1333     }
1334 
1335     len = strlenW( source ) + 1;
1336     if (absolute) len += strlenW( root ) + 1;
1337 
1338     if (!(p = buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1339     {
1340         HeapFree( GetProcessHeap(), 0, inf_source );
1341         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1342         return FALSE;
1343     }
1344 
1345     if (absolute)
1346     {
1347         strcpyW( buffer, root );
1348         p += strlenW( buffer );
1349         if (p[-1] != '\\') *p++ = '\\';
1350     }
1351     while (*source == '\\') source++;
1352     strcpyW( p, source );
1353 
1354     ret = do_file_copyW( buffer, dest, style, handler, context );
1355 
1356     HeapFree( GetProcessHeap(), 0, inf_source );
1357     HeapFree( GetProcessHeap(), 0, buffer );
1358     return ret;
1359 }
1360 
1361 /***********************************************************************
1362  *            SetupCommitFileQueueW   (SETUPAPI.@)
1363  */
1364 BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler,
1365                                    PVOID context )
1366 {
1367     struct file_queue *queue = handle;
1368     struct file_op *op;
1369     BOOL result = FALSE;
1370     FILEPATHS_W paths;
1371     UINT op_result;
1372 
1373     paths.Source = paths.Target = NULL;
1374 
1375     if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count)
1376         return TRUE;  /* nothing to do */
1377 
1378     if (!handler( context, SPFILENOTIFY_STARTQUEUE, (UINT_PTR)owner, 0 )) return FALSE;
1379 
1380     /* perform deletes */
1381 
1382     if (queue->delete_queue.count)
1383     {
1384         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE,
1385                        queue->delete_queue.count ))) goto done;
1386         for (op = queue->delete_queue.head; op; op = op->next)
1387         {
1388             build_filepathsW( op, &paths );
1389             op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE);
1390             if (op_result == FILEOP_ABORT) goto done;
1391             while (op_result == FILEOP_DOIT)
1392             {
1393                 TRACE( "deleting file %s\n", debugstr_w(paths.Target) );
1394                 if (DeleteFileW( paths.Target )) break;  /* success */
1395                 paths.Win32Error = GetLastError();
1396                 op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 );
1397                 if (op_result == FILEOP_ABORT) goto done;
1398             }
1399             handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 );
1400         }
1401         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 );
1402     }
1403 
1404     /* perform renames */
1405 
1406     if (queue->rename_queue.count)
1407     {
1408         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME,
1409                        queue->rename_queue.count ))) goto done;
1410         for (op = queue->rename_queue.head; op; op = op->next)
1411         {
1412             build_filepathsW( op, &paths );
1413             op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME);
1414             if (op_result == FILEOP_ABORT) goto done;
1415             while (op_result == FILEOP_DOIT)
1416             {
1417                 TRACE( "renaming file %s -> %s\n",
1418                        debugstr_w(paths.Source), debugstr_w(paths.Target) );
1419                 if (MoveFileW( paths.Source, paths.Target )) break;  /* success */
1420                 paths.Win32Error = GetLastError();
1421                 op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 );
1422                 if (op_result == FILEOP_ABORT) goto done;
1423             }
1424             handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 );
1425         }
1426         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 );
1427     }
1428 
1429     /* perform copies */
1430 
1431     if (queue->copy_queue.count)
1432     {
1433         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY,
1434                        queue->copy_queue.count ))) goto done;
1435         for (op = queue->copy_queue.head; op; op = op->next)
1436         {
1437             WCHAR newpath[MAX_PATH];
1438 
1439             build_filepathsW( op, &paths );
1440             op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY );
1441             if (op_result == FILEOP_ABORT) goto done;
1442             if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT;
1443             while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH)
1444             {
1445                 TRACE( "copying file %s -> %s\n",
1446                        debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ),
1447                        debugstr_w(paths.Target) );
1448                 if (op->dst_path)
1449                 {
1450                     if (!create_full_pathW( op->dst_path ))
1451                     {
1452                         paths.Win32Error = GetLastError();
1453                         op_result = handler( context, SPFILENOTIFY_COPYERROR,
1454                                      (UINT_PTR)&paths, (UINT_PTR)newpath );
1455                         if (op_result == FILEOP_ABORT) goto done;
1456                     }
1457                 }
1458                 if (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source,
1459                                paths.Target, op->style, handler, context )) break;  /* success */
1460                 /* try to extract it from the cabinet file */
1461                 if (op->src_tag)
1462                 {
1463                     if (extract_cabinet_file( op->src_tag, op->src_root,
1464                                               paths.Source, paths.Target )) break;
1465                 }
1466                 paths.Win32Error = GetLastError();
1467                 op_result = handler( context, SPFILENOTIFY_COPYERROR,
1468                                      (UINT_PTR)&paths, (UINT_PTR)newpath );
1469                 if (op_result == FILEOP_ABORT) goto done;
1470             }
1471             if (op->dst_sd)
1472             {
1473                 PSID psidOwner = NULL, psidGroup = NULL;
1474                 PACL pDacl = NULL, pSacl = NULL;
1475                 SECURITY_INFORMATION security_info = 0;
1476                 BOOL present, dummy;
1477 
1478                 if (GetSecurityDescriptorOwner( op->dst_sd, &psidOwner, &dummy ) && psidOwner)
1479                     security_info |= OWNER_SECURITY_INFORMATION;
1480                 if (GetSecurityDescriptorGroup( op->dst_sd, &psidGroup, &dummy ) && psidGroup)
1481                     security_info |= GROUP_SECURITY_INFORMATION;
1482                 if (GetSecurityDescriptorDacl( op->dst_sd, &present, &pDacl, &dummy ))
1483                     security_info |= DACL_SECURITY_INFORMATION;
1484                 if (GetSecurityDescriptorSacl( op->dst_sd, &present, &pSacl, &dummy ))
1485                     security_info |= DACL_SECURITY_INFORMATION;
1486                 SetNamedSecurityInfoW( (LPWSTR)paths.Target, SE_FILE_OBJECT, security_info,
1487                     psidOwner, psidGroup, pDacl, pSacl );
1488                 /* Yes, ignore the return code... */
1489             }
1490             handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 );
1491         }
1492         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 );
1493     }
1494 
1495 
1496     result = TRUE;
1497 
1498  done:
1499     handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 );
1500     HeapFree( GetProcessHeap(), 0, (void *)paths.Source );
1501     HeapFree( GetProcessHeap(), 0, (void *)paths.Target );
1502     return result;
1503 }
1504 
1505 
1506 /***********************************************************************
1507  *            SetupScanFileQueueA   (SETUPAPI.@)
1508  */
1509 BOOL WINAPI SetupScanFileQueueA( HSPFILEQ handle, DWORD flags, HWND window,
1510                                  PSP_FILE_CALLBACK_A handler, PVOID context, PDWORD result )
1511 {
1512     struct callback_WtoA_context ctx;
1513 
1514     TRACE("%p %x %p %p %p %p\n", handle, flags, window, handler, context, result);
1515 
1516     ctx.orig_context = context;
1517     ctx.orig_handler = handler;
1518 
1519     return SetupScanFileQueueW( handle, flags, window, QUEUE_callback_WtoA, &ctx, result );
1520 }
1521 
1522 
1523 /***********************************************************************
1524  *            SetupScanFileQueueW   (SETUPAPI.@)
1525  */
1526 BOOL WINAPI SetupScanFileQueueW( HSPFILEQ handle, DWORD flags, HWND window,
1527                                  PSP_FILE_CALLBACK_W handler, PVOID context, PDWORD result )
1528 {
1529     struct file_queue *queue = handle;
1530     struct file_op *op;
1531     FILEPATHS_W paths;
1532     UINT notification = 0;
1533     BOOL ret = FALSE;
1534 
1535     TRACE("%p %x %p %p %p %p\n", handle, flags, window, handler, context, result);
1536 
1537     *result = FALSE;
1538 
1539     if (!queue->copy_queue.count) return TRUE;
1540 
1541     if (flags & SPQ_SCAN_USE_CALLBACK)        notification = SPFILENOTIFY_QUEUESCAN;
1542     else if (flags & SPQ_SCAN_USE_CALLBACKEX) notification = SPFILENOTIFY_QUEUESCAN_EX;
1543 
1544     if (flags & ~(SPQ_SCAN_USE_CALLBACK | SPQ_SCAN_USE_CALLBACKEX))
1545     {
1546         FIXME("flags %x not fully implemented\n", flags);
1547     }
1548 
1549     paths.Source = paths.Target = NULL;
1550 
1551     for (op = queue->copy_queue.head; op; op = op->next)
1552     {
1553         build_filepathsW( op, &paths );
1554         switch (notification)
1555         {
1556         case SPFILENOTIFY_QUEUESCAN:
1557             /* FIXME: handle delay flag */
1558             if (handler( context,  notification, (UINT_PTR)paths.Target, 0 )) goto done;
1559             break;
1560         case SPFILENOTIFY_QUEUESCAN_EX:
1561             if (handler( context, notification, (UINT_PTR)&paths, 0 )) goto done;
1562             break;
1563         default:
1564             ret = TRUE; goto done;
1565         }
1566     }
1567 
1568     *result = TRUE;
1569 
1570  done:
1571     HeapFree( GetProcessHeap(), 0, (void *)paths.Source );
1572     HeapFree( GetProcessHeap(), 0, (void *)paths.Target );
1573     return ret;
1574 }
1575 
1576 
1577 /***********************************************************************
1578  *            SetupGetFileQueueCount   (SETUPAPI.@)
1579  */
1580 BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result )
1581 {
1582     struct file_queue *queue = handle;
1583 
1584     switch(op)
1585     {
1586     case FILEOP_COPY:
1587         *result = queue->copy_queue.count;
1588         return TRUE;
1589     case FILEOP_RENAME:
1590         *result = queue->rename_queue.count;
1591         return TRUE;
1592     case FILEOP_DELETE:
1593         *result = queue->delete_queue.count;
1594         return TRUE;
1595     }
1596     return FALSE;
1597 }
1598 
1599 
1600 /***********************************************************************
1601  *            SetupGetFileQueueFlags   (SETUPAPI.@)
1602  */
1603 BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags )
1604 {
1605     struct file_queue *queue = handle;
1606     *flags = queue->flags;
1607     return TRUE;
1608 }
1609 
1610 
1611 /***********************************************************************
1612  *            SetupSetFileQueueFlags   (SETUPAPI.@)
1613  */
1614 BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags )
1615 {
1616     struct file_queue *queue = handle;
1617     queue->flags = (queue->flags & ~mask) | flags;
1618     return TRUE;
1619 }
1620 
1621 
1622 /***********************************************************************
1623  *   SetupSetFileQueueAlternatePlatformA  (SETUPAPI.@)
1624  */
1625 BOOL WINAPI SetupSetFileQueueAlternatePlatformA(HSPFILEQ handle, PSP_ALTPLATFORM_INFO platform, PCSTR catalogfile)
1626 {
1627     FIXME("(%p, %p, %s) stub!\n", handle, platform, debugstr_a(catalogfile));
1628     return FALSE;
1629 }
1630 
1631 
1632 /***********************************************************************
1633  *   SetupSetFileQueueAlternatePlatformW  (SETUPAPI.@)
1634  */
1635 BOOL WINAPI SetupSetFileQueueAlternatePlatformW(HSPFILEQ handle, PSP_ALTPLATFORM_INFO platform, PCWSTR catalogfile)
1636 {
1637     FIXME("(%p, %p, %s) stub!\n", handle, platform, debugstr_w(catalogfile));
1638     return FALSE;
1639 }
1640 
1641 
1642 /***********************************************************************
1643  *            SetupInitDefaultQueueCallback   (SETUPAPI.@)
1644  */
1645 PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner )
1646 {
1647     return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL );
1648 }
1649 
1650 
1651 /***********************************************************************
1652  *            SetupInitDefaultQueueCallbackEx   (SETUPAPI.@)
1653  */
1654 PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg,
1655                                               DWORD reserved1, PVOID reserved2 )
1656 {
1657     struct default_callback_context *context;
1658 
1659     if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) )))
1660     {
1661         context->owner    = owner;
1662         context->progress = progress;
1663         context->message  = msg;
1664     }
1665     return context;
1666 }
1667 
1668 
1669 /***********************************************************************
1670  *            SetupTermDefaultQueueCallback   (SETUPAPI.@)
1671  */
1672 void WINAPI SetupTermDefaultQueueCallback( PVOID context )
1673 {
1674     HeapFree( GetProcessHeap(), 0, context );
1675 }
1676 
1677 
1678 /***********************************************************************
1679  *            SetupDefaultQueueCallbackA   (SETUPAPI.@)
1680  */
1681 UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification,
1682                                         UINT_PTR param1, UINT_PTR param2 )
1683 {
1684     FILEPATHS_A *paths = (FILEPATHS_A *)param1;
1685     struct default_callback_context *ctx = (struct default_callback_context *)context;
1686 
1687     switch(notification)
1688     {
1689     case SPFILENOTIFY_STARTQUEUE:
1690         TRACE( "start queue\n" );
1691         return TRUE;
1692     case SPFILENOTIFY_ENDQUEUE:
1693         TRACE( "end queue\n" );
1694         return 0;
1695     case SPFILENOTIFY_STARTSUBQUEUE:
1696         TRACE( "start subqueue %ld count %ld\n", param1, param2 );
1697         return TRUE;
1698     case SPFILENOTIFY_ENDSUBQUEUE:
1699         TRACE( "end subqueue %ld\n", param1 );
1700         return 0;
1701     case SPFILENOTIFY_STARTDELETE:
1702         TRACE( "start delete %s\n", debugstr_a(paths->Target) );
1703         return FILEOP_DOIT;
1704     case SPFILENOTIFY_ENDDELETE:
1705         TRACE( "end delete %s\n", debugstr_a(paths->Target) );
1706         return 0;
1707     case SPFILENOTIFY_DELETEERROR:
1708         /*Windows Ignores attempts to delete files / folders which do not exist*/
1709         if ((paths->Win32Error != ERROR_FILE_NOT_FOUND) && (paths->Win32Error != ERROR_PATH_NOT_FOUND))
1710         SetupDeleteErrorA(ctx->owner, NULL, paths->Target, paths->Win32Error, 0);
1711         return FILEOP_SKIP;
1712     case SPFILENOTIFY_STARTRENAME:
1713         TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1714         return FILEOP_DOIT;
1715     case SPFILENOTIFY_ENDRENAME:
1716         TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1717         return 0;
1718     case SPFILENOTIFY_RENAMEERROR:
1719         SetupRenameErrorA(ctx->owner, NULL, paths->Source, paths->Target, paths->Win32Error, 0);
1720         return FILEOP_SKIP;
1721     case SPFILENOTIFY_STARTCOPY:
1722         TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1723         return FILEOP_DOIT;
1724     case SPFILENOTIFY_ENDCOPY:
1725         TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1726         return 0;
1727     case SPFILENOTIFY_COPYERROR:
1728         ERR( "copy error %d %s -> %s\n", paths->Win32Error,
1729              debugstr_a(paths->Source), debugstr_a(paths->Target) );
1730         return FILEOP_SKIP;
1731     case SPFILENOTIFY_NEEDMEDIA:
1732         TRACE( "need media\n" );
1733         return FILEOP_SKIP;
1734     default:
1735         FIXME( "notification %d params %lx,%lx\n", notification, param1, param2 );
1736         break;
1737     }
1738     return 0;
1739 }
1740 
1741 
1742 /***********************************************************************
1743  *            SetupDefaultQueueCallbackW   (SETUPAPI.@)
1744  */
1745 UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification,
1746                                         UINT_PTR param1, UINT_PTR param2 )
1747 {
1748     FILEPATHS_W *paths = (FILEPATHS_W *)param1;
1749     struct default_callback_context *ctx = (struct default_callback_context *)context;
1750 
1751     switch(notification)
1752     {
1753     case SPFILENOTIFY_STARTQUEUE:
1754         TRACE( "start queue\n" );
1755         return TRUE;
1756     case SPFILENOTIFY_ENDQUEUE:
1757         TRACE( "end queue\n" );
1758         return 0;
1759     case SPFILENOTIFY_STARTSUBQUEUE:
1760         TRACE( "start subqueue %ld count %ld\n", param1, param2 );
1761         return TRUE;
1762     case SPFILENOTIFY_ENDSUBQUEUE:
1763         TRACE( "end subqueue %ld\n", param1 );
1764         return 0;
1765     case SPFILENOTIFY_STARTDELETE:
1766         TRACE( "start delete %s\n", debugstr_w(paths->Target) );
1767         return FILEOP_DOIT;
1768     case SPFILENOTIFY_ENDDELETE:
1769         TRACE( "end delete %s\n", debugstr_w(paths->Target) );
1770         return 0;
1771     case SPFILENOTIFY_DELETEERROR:
1772         /*Windows Ignores attempts to delete files / folders which do not exist*/
1773         if ((paths->Win32Error != ERROR_FILE_NOT_FOUND) && (paths->Win32Error != ERROR_PATH_NOT_FOUND))
1774             SetupDeleteErrorW(ctx->owner, NULL, paths->Target, paths->Win32Error, 0);
1775         return FILEOP_SKIP;
1776     case SPFILENOTIFY_STARTRENAME:
1777         SetupRenameErrorW(ctx->owner, NULL, paths->Source, paths->Target, paths->Win32Error, 0);
1778         return FILEOP_DOIT;
1779     case SPFILENOTIFY_ENDRENAME:
1780         TRACE( "end rename %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1781         return 0;
1782     case SPFILENOTIFY_RENAMEERROR:
1783         ERR( "rename error %d %s -> %s\n", paths->Win32Error,
1784              debugstr_w(paths->Source), debugstr_w(paths->Target) );
1785         return FILEOP_SKIP;
1786     case SPFILENOTIFY_STARTCOPY:
1787         TRACE( "start copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1788         return FILEOP_DOIT;
1789     case SPFILENOTIFY_ENDCOPY:
1790         TRACE( "end copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1791         return 0;
1792     case SPFILENOTIFY_COPYERROR:
1793         ERR( "copy error %d %s -> %s\n", paths->Win32Error,
1794              debugstr_w(paths->Source), debugstr_w(paths->Target) );
1795         return FILEOP_SKIP;
1796     case SPFILENOTIFY_NEEDMEDIA:
1797         TRACE( "need media\n" );
1798         return FILEOP_SKIP;
1799     default:
1800         FIXME( "notification %d params %lx,%lx\n", notification, param1, param2 );
1801         break;
1802     }
1803     return 0;
1804 }
1805 
1806 /***********************************************************************
1807  *            SetupDeleteErrorA   (SETUPAPI.@)
1808  */
1809 
1810 UINT WINAPI SetupDeleteErrorA( HWND parent, PCSTR dialogTitle, PCSTR file,
1811                                UINT w32error, DWORD style)
1812 {
1813     FIXME( "stub: (Error Number %d when attempting to delete %s)\n",
1814            w32error, debugstr_a(file) );
1815     return DPROMPT_SKIPFILE;
1816 }
1817 
1818 /***********************************************************************
1819  *            SetupDeleteErrorW   (SETUPAPI.@)
1820  */
1821 
1822 UINT WINAPI SetupDeleteErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR file,
1823                                UINT w32error, DWORD style)
1824 {
1825     FIXME( "stub: (Error Number %d when attempting to delete %s)\n",
1826            w32error, debugstr_w(file) );
1827     return DPROMPT_SKIPFILE;
1828 }
1829 
1830 /***********************************************************************
1831  *            SetupRenameErrorA   (SETUPAPI.@)
1832  */
1833 
1834 UINT WINAPI SetupRenameErrorA( HWND parent, PCSTR dialogTitle, PCSTR source,
1835                                PCSTR target, UINT w32error, DWORD style)
1836 {
1837     FIXME( "stub: (Error Number %d when attempting to rename %s to %s)\n",
1838            w32error, debugstr_a(source), debugstr_a(target));
1839     return DPROMPT_SKIPFILE;
1840 }
1841 
1842 /***********************************************************************
1843  *            SetupRenameErrorW   (SETUPAPI.@)
1844  */
1845 
1846 UINT WINAPI SetupRenameErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR source,
1847                                PCWSTR target, UINT w32error, DWORD style)
1848 {
1849     FIXME( "stub: (Error Number %d when attempting to rename %s to %s)\n",
1850            w32error, debugstr_w(source), debugstr_w(target));
1851     return DPROMPT_SKIPFILE;
1852 }
1853 
1854 
1855 /***********************************************************************
1856  *            SetupCopyErrorA   (SETUPAPI.@)
1857  */
1858 
1859 UINT WINAPI SetupCopyErrorA( HWND parent, PCSTR dialogTitle, PCSTR diskname,
1860                              PCSTR sourcepath, PCSTR sourcefile, PCSTR targetpath,
1861                              UINT w32error, DWORD style, PSTR pathbuffer,
1862                              DWORD buffersize, PDWORD requiredsize)
1863 {
1864     FIXME( "stub: (Error Number %d when attempting to copy file %s from %s to %s)\n",
1865            w32error, debugstr_a(sourcefile), debugstr_a(sourcepath) ,debugstr_a(targetpath));
1866     return DPROMPT_SKIPFILE;
1867 }
1868 
1869 /***********************************************************************
1870  *            SetupCopyErrorW   (SETUPAPI.@)
1871  */
1872 
1873 UINT WINAPI SetupCopyErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR diskname,
1874                              PCWSTR sourcepath, PCWSTR sourcefile, PCWSTR targetpath,
1875                              UINT w32error, DWORD style, PWSTR pathbuffer,
1876                              DWORD buffersize, PDWORD requiredsize)
1877 {
1878     FIXME( "stub: (Error Number %d when attempting to copy file %s from %s to %s)\n",
1879            w32error, debugstr_w(sourcefile), debugstr_w(sourcepath) ,debugstr_w(targetpath));
1880     return DPROMPT_SKIPFILE;
1881 }
1882 
1883 /***********************************************************************
1884  *            pSetupGetQueueFlags   (SETUPAPI.@)
1885  */
1886 DWORD WINAPI pSetupGetQueueFlags( HSPFILEQ handle )
1887 {
1888     struct file_queue *queue = handle;
1889     return queue->flags;
1890 }
1891 
1892 /***********************************************************************
1893  *            pSetupSetQueueFlags   (SETUPAPI.@)
1894  */
1895 BOOL WINAPI pSetupSetQueueFlags( HSPFILEQ handle, DWORD flags )
1896 {
1897     struct file_queue *queue = handle;
1898     queue->flags = flags;
1899     return TRUE;
1900 }
1901