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