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