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