Initialize Xlib threading support to see what it breaks...
[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     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_NEEDMEDIA:
250     case SPFILENOTIFY_QUEUESCAN:
251         FIXME("mapping for %d not implemented\n",notification);
252     case SPFILENOTIFY_STARTQUEUE:
253     case SPFILENOTIFY_ENDQUEUE:
254     case SPFILENOTIFY_STARTSUBQUEUE:
255     case SPFILENOTIFY_ENDSUBQUEUE:
256     default:
257         ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, param1, param2 );
258         break;
259     }
260         return ret;
261 }
262
263
264 /***********************************************************************
265  *            get_src_file_info
266  *
267  * Retrieve the source file information for a given file.
268  */
269 static void get_src_file_info( HINF hinf, struct file_op *op )
270 {
271     static const WCHAR SourceDisksNames[] =
272         {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0};
273     static const WCHAR SourceDisksFiles[] =
274         {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0};
275
276     INFCONTEXT file_ctx, disk_ctx;
277     INT id, diskid;
278     DWORD len, len2;
279
280     /* find the SourceDisksFiles entry */
281     if (!SetupFindFirstLineW( hinf, SourceDisksFiles, op->src_file, &file_ctx ))
282     {
283         const WCHAR *dir;
284
285         if ((op->style & (SP_COPY_SOURCE_ABSOLUTE|SP_COPY_SOURCEPATH_ABSOLUTE))) return;
286         /* no specific info, use .inf file source directory */
287         if (!op->src_root && (dir = DIRID_get_string( hinf, DIRID_SRCPATH )))
288             op->src_root = strdupW( dir );
289         return;
290     }
291     if (!SetupGetIntField( &file_ctx, 1, &diskid )) return;
292
293     /* now find the diskid in the SourceDisksNames section */
294     if (!SetupFindFirstLineW( hinf, SourceDisksNames, NULL, &disk_ctx )) return;
295     for (;;)
296     {
297         if (SetupGetIntField( &disk_ctx, 0, &id ) && (id == diskid)) break;
298         if (!SetupFindNextLine( &disk_ctx, &disk_ctx )) return;
299     }
300
301     /* and fill in the missing info */
302
303     if (!op->src_descr)
304     {
305         if (SetupGetStringFieldW( &disk_ctx, 1, NULL, 0, &len ) &&
306             (op->src_descr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) )))
307             SetupGetStringFieldW( &disk_ctx, 1, op->src_descr, len, NULL );
308     }
309     if (!op->src_tag)
310     {
311         if (SetupGetStringFieldW( &disk_ctx, 2, NULL, 0, &len ) &&
312             (op->src_tag = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) )))
313             SetupGetStringFieldW( &disk_ctx, 2, op->src_tag, len, NULL );
314     }
315     if (!op->src_path && !(op->style & SP_COPY_SOURCE_ABSOLUTE))
316     {
317         if (!(op->style & SP_COPY_SOURCEPATH_ABSOLUTE))
318         {
319             /* retrieve relative path for this disk */
320             if (!SetupGetStringFieldW( &disk_ctx, 4, NULL, 0, &len )) len = 0;
321         }
322         /* retrieve relative path for this file */
323         if (!SetupGetStringFieldW( &file_ctx, 2, NULL, 0, &len2 )) len2 = 0;
324
325         if ((len || len2) &&
326             (op->src_path = HeapAlloc( GetProcessHeap(), 0, (len+len2)*sizeof(WCHAR) )))
327         {
328             WCHAR *ptr = op->src_path;
329             if (len)
330             {
331                 SetupGetStringFieldW( &disk_ctx, 4, op->src_path, len, NULL );
332                 ptr = op->src_path + strlenW(op->src_path);
333                 if (len2 && ptr > op->src_path && ptr[-1] != '\\') *ptr++ = '\\';
334             }
335             if (!SetupGetStringFieldW( &disk_ctx, 4, ptr, len2, NULL )) *ptr = 0;
336         }
337     }
338     if (!op->src_root) op->src_root = strdupW( PARSER_get_src_root(hinf) );
339 }
340
341
342 /***********************************************************************
343  *            get_destination_dir
344  *
345  * Retrieve the destination dir for a given section.
346  */
347 static WCHAR *get_destination_dir( HINF hinf, const WCHAR *section )
348 {
349     static const WCHAR Dest[] = {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0};
350     static const WCHAR Def[]  = {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0};
351
352     const WCHAR *dir;
353     WCHAR *ptr, *ret;
354     INFCONTEXT context;
355     INT dirid;
356     DWORD len1, len2;
357
358     if (!SetupFindFirstLineW( hinf, Dest, section, &context ) &&
359         !SetupFindFirstLineW( hinf, Dest, Def, &context )) return NULL;
360     if (!SetupGetIntField( &context, 1, &dirid )) return NULL;
361     if (!(dir = DIRID_get_string( hinf, dirid ))) return NULL;
362     len1 = strlenW(dir) + 1;
363     if (!SetupGetStringFieldW( &context, 2, NULL, 0, &len2 )) len2 = 0;
364     if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len1+len2) * sizeof(WCHAR) ))) return NULL;
365     strcpyW( ret, dir );
366     ptr = ret + strlenW(ret);
367     if (len2 && ptr > ret && ptr[-1] != '\\') *ptr++ = '\\';
368     if (!SetupGetStringFieldW( &context, 2, ptr, len2, NULL )) *ptr = 0;
369     return ret;
370 }
371
372
373 static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD );
374
375 /***********************************************************************
376  *            extract_cabinet_file
377  *
378  * Extract a file from a .cab file.
379  */
380 static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root,
381                                   const WCHAR *src, const WCHAR *dst )
382 {
383     static const WCHAR extW[] = {'.','c','a','b',0};
384     static HMODULE advpack;
385
386     char *cab_path, *cab_file;
387     int len = strlenW( cabinet );
388
389     /* make sure the cabinet file has a .cab extension */
390     if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE;
391     if (!pExtractFiles)
392     {
393         if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" )))
394         {
395             ERR( "could not load advpack.dll\n" );
396             return FALSE;
397         }
398         if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles" )))
399         {
400             ERR( "could not find ExtractFiles in advpack.dll\n" );
401             return FALSE;
402         }
403     }
404
405     if (!(cab_path = strdupWtoA( root ))) return FALSE;
406     len = WideCharToMultiByte( CP_ACP, 0, cabinet, -1, NULL, 0, NULL, NULL );
407     if (!(cab_file = HeapAlloc( GetProcessHeap(), 0, strlen(cab_path) + len + 1 )))
408     {
409         HeapFree( GetProcessHeap(), 0, cab_path );
410         return FALSE;
411     }
412     strcpy( cab_file, cab_path );
413     if (cab_file[0] && cab_file[strlen(cab_file)-1] != '\\') strcat( cab_file, "\\" );
414     WideCharToMultiByte( CP_ACP, 0, cabinet, -1, cab_file + strlen(cab_file), len, NULL, NULL );
415     FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) );
416     pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 );
417     HeapFree( GetProcessHeap(), 0, cab_file );
418     HeapFree( GetProcessHeap(), 0, cab_path );
419     return CopyFileW( src, dst, FALSE /*FIXME*/ );
420 }
421
422
423 /***********************************************************************
424  *            SetupOpenFileQueue   (SETUPAPI.@)
425  */
426 HSPFILEQ WINAPI SetupOpenFileQueue(void)
427 {
428     struct file_queue *queue;
429
430     if (!(queue = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*queue))))
431         return (HSPFILEQ)INVALID_HANDLE_VALUE;
432     return queue;
433 }
434
435
436 /***********************************************************************
437  *            SetupCloseFileQueue   (SETUPAPI.@)
438  */
439 BOOL WINAPI SetupCloseFileQueue( HSPFILEQ handle )
440 {
441     struct file_queue *queue = handle;
442
443     free_file_op_queue( &queue->copy_queue );
444     free_file_op_queue( &queue->rename_queue );
445     free_file_op_queue( &queue->delete_queue );
446     HeapFree( GetProcessHeap(), 0, queue );
447     return TRUE;
448 }
449
450
451 /***********************************************************************
452  *            SetupQueueCopyIndirectA   (SETUPAPI.@)
453  */
454 BOOL WINAPI SetupQueueCopyIndirectA( PSP_FILE_COPY_PARAMS_A params )
455 {
456     struct file_queue *queue = params->QueueHandle;
457     struct file_op *op;
458
459     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
460     op->style      = params->CopyStyle;
461     op->src_root   = strdupAtoW( params->SourceRootPath );
462     op->src_path   = strdupAtoW( params->SourcePath );
463     op->src_file   = strdupAtoW( params->SourceFilename );
464     op->src_descr  = strdupAtoW( params->SourceDescription );
465     op->src_tag    = strdupAtoW( params->SourceTagfile );
466     op->dst_path   = strdupAtoW( params->TargetDirectory );
467     op->dst_file   = strdupAtoW( params->TargetFilename );
468
469     /* some defaults */
470     if (!op->src_file) op->src_file = op->dst_file;
471     if (params->LayoutInf)
472     {
473         get_src_file_info( params->LayoutInf, op );
474         if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file );
475     }
476
477     TRACE( "root=%s path=%s file=%s -> dir=%s file=%s  descr=%s tag=%s\n",
478            debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file),
479            debugstr_w(op->dst_path), debugstr_w(op->dst_file),
480            debugstr_w(op->src_descr), debugstr_w(op->src_tag) );
481
482     queue_file_op( &queue->copy_queue, op );
483     return TRUE;
484 }
485
486
487 /***********************************************************************
488  *            SetupQueueCopyIndirectW   (SETUPAPI.@)
489  */
490 BOOL WINAPI SetupQueueCopyIndirectW( PSP_FILE_COPY_PARAMS_W params )
491 {
492     struct file_queue *queue = params->QueueHandle;
493     struct file_op *op;
494
495     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
496     op->style      = params->CopyStyle;
497     op->src_root   = strdupW( params->SourceRootPath );
498     op->src_path   = strdupW( params->SourcePath );
499     op->src_file   = strdupW( params->SourceFilename );
500     op->src_descr  = strdupW( params->SourceDescription );
501     op->src_tag    = strdupW( params->SourceTagfile );
502     op->dst_path   = strdupW( params->TargetDirectory );
503     op->dst_file   = strdupW( params->TargetFilename );
504
505     /* some defaults */
506     if (!op->src_file) op->src_file = op->dst_file;
507     if (params->LayoutInf)
508     {
509         get_src_file_info( params->LayoutInf, op );
510         if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file );
511     }
512
513     TRACE( "root=%s path=%s file=%s -> dir=%s file=%s  descr=%s tag=%s\n",
514            debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file),
515            debugstr_w(op->dst_path), debugstr_w(op->dst_file),
516            debugstr_w(op->src_descr), debugstr_w(op->src_tag) );
517
518     queue_file_op( &queue->copy_queue, op );
519     return TRUE;
520 }
521
522
523 /***********************************************************************
524  *            SetupQueueCopyA   (SETUPAPI.@)
525  */
526 BOOL WINAPI SetupQueueCopyA( HSPFILEQ queue, PCSTR src_root, PCSTR src_path, PCSTR src_file,
527                              PCSTR src_descr, PCSTR src_tag, PCSTR dst_dir, PCSTR dst_file,
528                              DWORD style )
529 {
530     SP_FILE_COPY_PARAMS_A params;
531
532     params.cbSize             = sizeof(params);
533     params.QueueHandle        = queue;
534     params.SourceRootPath     = src_root;
535     params.SourcePath         = src_path;
536     params.SourceFilename     = src_file;
537     params.SourceDescription  = src_descr;
538     params.SourceTagfile      = src_tag;
539     params.TargetDirectory    = dst_dir;
540     params.TargetFilename     = dst_file;
541     params.CopyStyle          = style;
542     params.LayoutInf          = 0;
543     params.SecurityDescriptor = NULL;
544     return SetupQueueCopyIndirectA( &params );
545 }
546
547
548 /***********************************************************************
549  *            SetupQueueCopyW   (SETUPAPI.@)
550  */
551 BOOL WINAPI SetupQueueCopyW( HSPFILEQ queue, PCWSTR src_root, PCWSTR src_path, PCWSTR src_file,
552                              PCWSTR src_descr, PCWSTR src_tag, PCWSTR dst_dir, PCWSTR dst_file,
553                              DWORD style )
554 {
555     SP_FILE_COPY_PARAMS_W params;
556
557     params.cbSize             = sizeof(params);
558     params.QueueHandle        = queue;
559     params.SourceRootPath     = src_root;
560     params.SourcePath         = src_path;
561     params.SourceFilename     = src_file;
562     params.SourceDescription  = src_descr;
563     params.SourceTagfile      = src_tag;
564     params.TargetDirectory    = dst_dir;
565     params.TargetFilename     = dst_file;
566     params.CopyStyle          = style;
567     params.LayoutInf          = 0;
568     params.SecurityDescriptor = NULL;
569     return SetupQueueCopyIndirectW( &params );
570 }
571
572
573 /***********************************************************************
574  *            SetupQueueDefaultCopyA   (SETUPAPI.@)
575  */
576 BOOL WINAPI SetupQueueDefaultCopyA( HSPFILEQ queue, HINF hinf, PCSTR src_root, PCSTR src_file,
577                                     PCSTR dst_file, DWORD style )
578 {
579     SP_FILE_COPY_PARAMS_A params;
580
581     params.cbSize             = sizeof(params);
582     params.QueueHandle        = queue;
583     params.SourceRootPath     = src_root;
584     params.SourcePath         = NULL;
585     params.SourceFilename     = src_file;
586     params.SourceDescription  = NULL;
587     params.SourceTagfile      = NULL;
588     params.TargetDirectory    = NULL;
589     params.TargetFilename     = dst_file;
590     params.CopyStyle          = style;
591     params.LayoutInf          = hinf;
592     params.SecurityDescriptor = NULL;
593     return SetupQueueCopyIndirectA( &params );
594 }
595
596
597 /***********************************************************************
598  *            SetupQueueDefaultCopyW   (SETUPAPI.@)
599  */
600 BOOL WINAPI SetupQueueDefaultCopyW( HSPFILEQ queue, HINF hinf, PCWSTR src_root, PCWSTR src_file,
601                                     PCWSTR dst_file, DWORD style )
602 {
603     SP_FILE_COPY_PARAMS_W params;
604
605     params.cbSize             = sizeof(params);
606     params.QueueHandle        = queue;
607     params.SourceRootPath     = src_root;
608     params.SourcePath         = NULL;
609     params.SourceFilename     = src_file;
610     params.SourceDescription  = NULL;
611     params.SourceTagfile      = NULL;
612     params.TargetDirectory    = NULL;
613     params.TargetFilename     = dst_file;
614     params.CopyStyle          = style;
615     params.LayoutInf          = hinf;
616     params.SecurityDescriptor = NULL;
617     return SetupQueueCopyIndirectW( &params );
618 }
619
620
621 /***********************************************************************
622  *            SetupQueueDeleteA   (SETUPAPI.@)
623  */
624 BOOL WINAPI SetupQueueDeleteA( HSPFILEQ handle, PCSTR part1, PCSTR part2 )
625 {
626     struct file_queue *queue = handle;
627     struct file_op *op;
628
629     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
630     op->style      = 0;
631     op->src_root   = NULL;
632     op->src_path   = NULL;
633     op->src_file   = NULL;
634     op->src_descr  = NULL;
635     op->src_tag    = NULL;
636     op->dst_path   = strdupAtoW( part1 );
637     op->dst_file   = strdupAtoW( part2 );
638     queue_file_op( &queue->delete_queue, op );
639     return TRUE;
640 }
641
642
643 /***********************************************************************
644  *            SetupQueueDeleteW   (SETUPAPI.@)
645  */
646 BOOL WINAPI SetupQueueDeleteW( HSPFILEQ handle, PCWSTR part1, PCWSTR part2 )
647 {
648     struct file_queue *queue = handle;
649     struct file_op *op;
650
651     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
652     op->style      = 0;
653     op->src_root   = NULL;
654     op->src_path   = NULL;
655     op->src_file   = NULL;
656     op->src_descr  = NULL;
657     op->src_tag    = NULL;
658     op->dst_path   = strdupW( part1 );
659     op->dst_file   = strdupW( part2 );
660     queue_file_op( &queue->delete_queue, op );
661     return TRUE;
662 }
663
664
665 /***********************************************************************
666  *            SetupQueueRenameA   (SETUPAPI.@)
667  */
668 BOOL WINAPI SetupQueueRenameA( HSPFILEQ handle, PCSTR SourcePath, PCSTR SourceFilename,
669                                PCSTR TargetPath, PCSTR TargetFilename )
670 {
671     struct file_queue *queue = handle;
672     struct file_op *op;
673
674     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
675     op->style      = 0;
676     op->src_root   = NULL;
677     op->src_path   = strdupAtoW( SourcePath );
678     op->src_file   = strdupAtoW( SourceFilename );
679     op->src_descr  = NULL;
680     op->src_tag    = NULL;
681     op->dst_path   = strdupAtoW( TargetPath );
682     op->dst_file   = strdupAtoW( TargetFilename );
683     queue_file_op( &queue->rename_queue, op );
684     return TRUE;
685 }
686
687
688 /***********************************************************************
689  *            SetupQueueRenameW   (SETUPAPI.@)
690  */
691 BOOL WINAPI SetupQueueRenameW( HSPFILEQ handle, PCWSTR SourcePath, PCWSTR SourceFilename,
692                                PCWSTR TargetPath, PCWSTR TargetFilename )
693 {
694     struct file_queue *queue = handle;
695     struct file_op *op;
696
697     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
698     op->style      = 0;
699     op->src_root   = NULL;
700     op->src_path   = strdupW( SourcePath );
701     op->src_file   = strdupW( SourceFilename );
702     op->src_descr  = NULL;
703     op->src_tag    = NULL;
704     op->dst_path   = strdupW( TargetPath );
705     op->dst_file   = strdupW( TargetFilename );
706     queue_file_op( &queue->rename_queue, op );
707     return TRUE;
708 }
709
710
711 /***********************************************************************
712  *            SetupQueueCopySectionA   (SETUPAPI.@)
713  */
714 BOOL WINAPI SetupQueueCopySectionA( HSPFILEQ queue, PCSTR src_root, HINF hinf, HINF hlist,
715                                     PCSTR section, DWORD style )
716 {
717     UNICODE_STRING sectionW;
718     BOOL ret = FALSE;
719
720     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
721     {
722         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
723         return FALSE;
724     }
725     if (!src_root)
726         ret = SetupQueueCopySectionW( queue, NULL, hinf, hlist, sectionW.Buffer, style );
727     else
728     {
729         UNICODE_STRING srcW;
730         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
731         {
732             ret = SetupQueueCopySectionW( queue, srcW.Buffer, hinf, hlist, sectionW.Buffer, style );
733             RtlFreeUnicodeString( &srcW );
734         }
735         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
736     }
737     RtlFreeUnicodeString( &sectionW );
738     return ret;
739 }
740
741
742 /***********************************************************************
743  *            SetupQueueCopySectionW   (SETUPAPI.@)
744  */
745 BOOL WINAPI SetupQueueCopySectionW( HSPFILEQ queue, PCWSTR src_root, HINF hinf, HINF hlist,
746                                     PCWSTR section, DWORD style )
747 {
748     SP_FILE_COPY_PARAMS_W params;
749     INFCONTEXT context;
750     WCHAR dest[MAX_PATH], src[MAX_PATH];
751     INT flags;
752
753     TRACE( "hinf=%p/%p section=%s root=%s\n",
754            hinf, hlist, debugstr_w(section), debugstr_w(src_root) );
755
756     params.cbSize             = sizeof(params);
757     params.QueueHandle        = queue;
758     params.SourceRootPath     = src_root;
759     params.SourcePath         = NULL;
760     params.SourceDescription  = NULL;
761     params.SourceTagfile      = NULL;
762     params.TargetFilename     = dest;
763     params.CopyStyle          = style;
764     params.LayoutInf          = hinf;
765     params.SecurityDescriptor = NULL;
766
767     if (!hlist) hlist = hinf;
768     if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
769     if (!(params.TargetDirectory = get_destination_dir( hinf, section ))) return FALSE;
770     do
771     {
772         if (!SetupGetStringFieldW( &context, 1, dest, sizeof(dest)/sizeof(WCHAR), NULL ))
773             return FALSE;
774         if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) *src = 0;
775         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;  /* FIXME */
776
777         params.SourceFilename = *src ? src : NULL;
778         if (!SetupQueueCopyIndirectW( &params )) return FALSE;
779     } while (SetupFindNextLine( &context, &context ));
780     return TRUE;
781 }
782
783
784 /***********************************************************************
785  *            SetupQueueDeleteSectionA   (SETUPAPI.@)
786  */
787 BOOL WINAPI SetupQueueDeleteSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
788 {
789     UNICODE_STRING sectionW;
790     BOOL ret = FALSE;
791
792     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
793     {
794         ret = SetupQueueDeleteSectionW( queue, hinf, hlist, sectionW.Buffer );
795         RtlFreeUnicodeString( &sectionW );
796     }
797     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
798     return ret;
799 }
800
801
802 /***********************************************************************
803  *            SetupQueueDeleteSectionW   (SETUPAPI.@)
804  */
805 BOOL WINAPI SetupQueueDeleteSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
806 {
807     INFCONTEXT context;
808     WCHAR *dest_dir;
809     WCHAR buffer[MAX_PATH];
810     BOOL ret = FALSE;
811     INT flags;
812
813     TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
814
815     if (!hlist) hlist = hinf;
816     if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
817     if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
818     do
819     {
820         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
821             goto done;
822         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
823         if (!SetupQueueDeleteW( queue, dest_dir, buffer )) goto done;
824     } while (SetupFindNextLine( &context, &context ));
825
826     ret = TRUE;
827  done:
828     HeapFree( GetProcessHeap(), 0, dest_dir );
829     return ret;
830 }
831
832
833 /***********************************************************************
834  *            SetupQueueRenameSectionA   (SETUPAPI.@)
835  */
836 BOOL WINAPI SetupQueueRenameSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
837 {
838     UNICODE_STRING sectionW;
839     BOOL ret = FALSE;
840
841     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
842     {
843         ret = SetupQueueRenameSectionW( queue, hinf, hlist, sectionW.Buffer );
844         RtlFreeUnicodeString( &sectionW );
845     }
846     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
847     return ret;
848 }
849
850
851 /***********************************************************************
852  *            SetupQueueRenameSectionW   (SETUPAPI.@)
853  */
854 BOOL WINAPI SetupQueueRenameSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
855 {
856     INFCONTEXT context;
857     WCHAR *dest_dir;
858     WCHAR src[MAX_PATH], dst[MAX_PATH];
859     BOOL ret = FALSE;
860
861     TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
862
863     if (!hlist) hlist = hinf;
864     if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
865     if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
866     do
867     {
868         if (!SetupGetStringFieldW( &context, 1, dst, sizeof(dst)/sizeof(WCHAR), NULL ))
869             goto done;
870         if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL ))
871             goto done;
872         if (!SetupQueueRenameW( queue, dest_dir, src, NULL, dst )) goto done;
873     } while (SetupFindNextLine( &context, &context ));
874
875     ret = TRUE;
876  done:
877     HeapFree( GetProcessHeap(), 0, dest_dir );
878     return ret;
879 }
880
881
882 /***********************************************************************
883  *            SetupCommitFileQueueA   (SETUPAPI.@)
884  */
885 BOOL WINAPI SetupCommitFileQueueA( HWND owner, HSPFILEQ queue, PSP_FILE_CALLBACK_A handler,
886                                    PVOID context )
887 {
888     struct callback_WtoA_context ctx;
889
890     ctx.orig_context = context;
891     ctx.orig_handler = handler;
892     return SetupCommitFileQueueW( owner, queue, QUEUE_callback_WtoA, &ctx );
893 }
894
895
896 /***********************************************************************
897  *            create_full_pathW
898  *
899  * Recursively create all directories in the path.
900  */
901 static BOOL create_full_pathW(const WCHAR *path)
902 {
903     BOOL ret = TRUE;
904     int len;
905     WCHAR *new_path;
906
907     new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR));
908     strcpyW(new_path, path);
909
910     while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
911         new_path[len - 1] = 0;
912
913     while(!CreateDirectoryW(new_path, NULL))
914     {
915         WCHAR *slash;
916         DWORD last_error = GetLastError();
917
918         if(last_error == ERROR_ALREADY_EXISTS)
919             break;
920
921         if(last_error != ERROR_PATH_NOT_FOUND)
922         {
923             ret = FALSE;
924             break;
925         }
926
927         if(!(slash = strrchrW(new_path, '\\')))
928         {
929             ret = FALSE;
930             break;
931         }
932
933         len = slash - new_path;
934         new_path[len] = 0;
935         if(!create_full_pathW(new_path))
936         {
937             ret = FALSE;
938             break;
939         }
940         new_path[len] = '\\';
941     }
942
943     HeapFree(GetProcessHeap(), 0, new_path);
944     return ret;
945 }
946
947 BOOL static do_file_copyW( LPCWSTR source, LPCWSTR target, DWORD style)
948 {
949     BOOL rc = FALSE;
950     BOOL docopy = TRUE;
951
952     TRACE("copy %s to %s style 0x%lx\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 %li ... SizeSource %li\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             INT 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                     TRACE("Versions: Source %li.%li target %li.%li\n",
1015                       SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS,
1016                       TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS);
1017
1018                     if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS)
1019                     {
1020                         FIXME("Notify that target version is greater..\n");
1021                         docopy = FALSE;
1022                     }
1023                     else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS)
1024                              && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS))
1025                     {
1026                         FIXME("Notify that target version is greater..\n");
1027                         docopy = FALSE;
1028                     }
1029                     else if ((style & SP_COPY_NEWER_ONLY) &&
1030                         (TargetInfo->dwFileVersionMS ==
1031                          SourceInfo->dwFileVersionMS)
1032                         &&(TargetInfo->dwFileVersionLS ==
1033                         SourceInfo->dwFileVersionLS))
1034                     {
1035                         FIXME("Notify that target version is greater..\n");
1036                         docopy = FALSE;
1037                     }
1038                 }
1039             }
1040             HeapFree(GetProcessHeap(),0,VersionSource);
1041             HeapFree(GetProcessHeap(),0,VersionTarget);
1042         }
1043     }
1044     if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE))
1045     {
1046         if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES)
1047         {
1048             FIXME("Notify user target file exists\n");
1049             docopy = FALSE;
1050         }
1051     }
1052     if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE |
1053                  SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP))
1054     {
1055         ERR("Unsupported style(s) 0x%lx\n",style);
1056     }
1057
1058     if (docopy)
1059     {
1060         rc = CopyFileW(source,target,FALSE);
1061         TRACE("Did copy... rc was %i\n",rc);
1062     }
1063
1064     /* after copy processing */
1065     if (style & SP_COPY_DELETESOURCE)
1066     {
1067        if (rc)
1068             DeleteFileW(source);
1069     }
1070
1071     return rc;
1072 }
1073
1074 /***********************************************************************
1075  *            SetupCommitFileQueueW   (SETUPAPI.@)
1076  */
1077 BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler,
1078                                    PVOID context )
1079 {
1080     struct file_queue *queue = handle;
1081     struct file_op *op;
1082     BOOL result = FALSE;
1083     FILEPATHS_W paths;
1084     UINT op_result;
1085
1086     paths.Source = paths.Target = NULL;
1087
1088     if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count)
1089         return TRUE;  /* nothing to do */
1090
1091     if (!handler( context, SPFILENOTIFY_STARTQUEUE, (UINT)owner, 0 )) return FALSE;
1092
1093     /* perform deletes */
1094
1095     if (queue->delete_queue.count)
1096     {
1097         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE,
1098                        queue->delete_queue.count ))) goto done;
1099         for (op = queue->delete_queue.head; op; op = op->next)
1100         {
1101             build_filepathsW( op, &paths );
1102             op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE);
1103             if (op_result == FILEOP_ABORT) goto done;
1104             while (op_result == FILEOP_DOIT)
1105             {
1106                 TRACE( "deleting file %s\n", debugstr_w(paths.Target) );
1107                 if (DeleteFileW( paths.Target )) break;  /* success */
1108                 paths.Win32Error = GetLastError();
1109                 op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 );
1110                 if (op_result == FILEOP_ABORT) goto done;
1111             }
1112             handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 );
1113         }
1114         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 );
1115     }
1116
1117     /* perform renames */
1118
1119     if (queue->rename_queue.count)
1120     {
1121         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME,
1122                        queue->rename_queue.count ))) goto done;
1123         for (op = queue->rename_queue.head; op; op = op->next)
1124         {
1125             build_filepathsW( op, &paths );
1126             op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME);
1127             if (op_result == FILEOP_ABORT) goto done;
1128             while (op_result == FILEOP_DOIT)
1129             {
1130                 TRACE( "renaming file %s -> %s\n",
1131                        debugstr_w(paths.Source), debugstr_w(paths.Target) );
1132                 if (MoveFileW( paths.Source, paths.Target )) break;  /* success */
1133                 paths.Win32Error = GetLastError();
1134                 op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 );
1135                 if (op_result == FILEOP_ABORT) goto done;
1136             }
1137             handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 );
1138         }
1139         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 );
1140     }
1141
1142     /* perform copies */
1143
1144     if (queue->copy_queue.count)
1145     {
1146         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY,
1147                        queue->copy_queue.count ))) goto done;
1148         for (op = queue->copy_queue.head; op; op = op->next)
1149         {
1150             WCHAR newpath[MAX_PATH];
1151
1152             build_filepathsW( op, &paths );
1153             op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY );
1154             if (op_result == FILEOP_ABORT) goto done;
1155             if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT;
1156             while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH)
1157             {
1158                 TRACE( "copying file %s -> %s\n",
1159                        debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ),
1160                        debugstr_w(paths.Target) );
1161                 if (op->dst_path)
1162                 {
1163                     if (!create_full_pathW( op->dst_path ))
1164                     {
1165                         paths.Win32Error = GetLastError();
1166                         op_result = handler( context, SPFILENOTIFY_COPYERROR,
1167                                              (UINT_PTR)&paths, (UINT_PTR)newpath );
1168                         if (op_result == FILEOP_ABORT) goto done;
1169                     }
1170                 }
1171                 if (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source,
1172                                paths.Target, op->style )) break;  /* success */
1173                 /* try to extract it from the cabinet file */
1174                 if (op->src_tag)
1175                 {
1176                     if (extract_cabinet_file( op->src_tag, op->src_root,
1177                                               paths.Source, paths.Target )) break;
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             handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 );
1185         }
1186         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 );
1187     }
1188
1189
1190     result = TRUE;
1191
1192  done:
1193     handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 );
1194     HeapFree( GetProcessHeap(), 0, (void *)paths.Source );
1195     HeapFree( GetProcessHeap(), 0, (void *)paths.Target );
1196     return result;
1197 }
1198
1199
1200 /***********************************************************************
1201  *            SetupScanFileQueueA   (SETUPAPI.@)
1202  */
1203 BOOL WINAPI SetupScanFileQueueA( HSPFILEQ queue, DWORD flags, HWND window,
1204                                  PSP_FILE_CALLBACK_A callback, PVOID context, PDWORD result )
1205 {
1206     FIXME("stub\n");
1207     return FALSE;
1208 }
1209
1210
1211 /***********************************************************************
1212  *            SetupScanFileQueueW   (SETUPAPI.@)
1213  */
1214 BOOL WINAPI SetupScanFileQueueW( HSPFILEQ queue, DWORD flags, HWND window,
1215                                  PSP_FILE_CALLBACK_W callback, PVOID context, PDWORD result )
1216 {
1217     FIXME("stub\n");
1218     return FALSE;
1219 }
1220
1221
1222 /***********************************************************************
1223  *            SetupGetFileQueueCount   (SETUPAPI.@)
1224  */
1225 BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result )
1226 {
1227     struct file_queue *queue = handle;
1228
1229     switch(op)
1230     {
1231     case FILEOP_COPY:
1232         *result = queue->copy_queue.count;
1233         return TRUE;
1234     case FILEOP_RENAME:
1235         *result = queue->rename_queue.count;
1236         return TRUE;
1237     case FILEOP_DELETE:
1238         *result = queue->delete_queue.count;
1239         return TRUE;
1240     }
1241     return FALSE;
1242 }
1243
1244
1245 /***********************************************************************
1246  *            SetupGetFileQueueFlags   (SETUPAPI.@)
1247  */
1248 BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags )
1249 {
1250     struct file_queue *queue = handle;
1251     *flags = queue->flags;
1252     return TRUE;
1253 }
1254
1255
1256 /***********************************************************************
1257  *            SetupSetFileQueueFlags   (SETUPAPI.@)
1258  */
1259 BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags )
1260 {
1261     struct file_queue *queue = handle;
1262     queue->flags = (queue->flags & ~mask) | flags;
1263     return TRUE;
1264 }
1265
1266
1267 /***********************************************************************
1268  *            SetupInitDefaultQueueCallback   (SETUPAPI.@)
1269  */
1270 PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner )
1271 {
1272     return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL );
1273 }
1274
1275
1276 /***********************************************************************
1277  *            SetupInitDefaultQueueCallbackEx   (SETUPAPI.@)
1278  */
1279 PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg,
1280                                               DWORD reserved1, PVOID reserved2 )
1281 {
1282     struct default_callback_context *context;
1283
1284     if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) )))
1285     {
1286         context->owner    = owner;
1287         context->progress = progress;
1288         context->message  = msg;
1289     }
1290     return context;
1291 }
1292
1293
1294 /***********************************************************************
1295  *            SetupTermDefaultQueueCallback   (SETUPAPI.@)
1296  */
1297 void WINAPI SetupTermDefaultQueueCallback( PVOID context )
1298 {
1299     HeapFree( GetProcessHeap(), 0, context );
1300 }
1301
1302
1303 /***********************************************************************
1304  *            SetupDefaultQueueCallbackA   (SETUPAPI.@)
1305  */
1306 UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification,
1307                                         UINT_PTR param1, UINT_PTR param2 )
1308 {
1309     FILEPATHS_A *paths = (FILEPATHS_A *)param1;
1310
1311     switch(notification)
1312     {
1313     case SPFILENOTIFY_STARTQUEUE:
1314         TRACE( "start queue\n" );
1315         return TRUE;
1316     case SPFILENOTIFY_ENDQUEUE:
1317         TRACE( "end queue\n" );
1318         return 0;
1319     case SPFILENOTIFY_STARTSUBQUEUE:
1320         TRACE( "start subqueue %d count %d\n", param1, param2 );
1321         return TRUE;
1322     case SPFILENOTIFY_ENDSUBQUEUE:
1323         TRACE( "end subqueue %d\n", param1 );
1324         return 0;
1325     case SPFILENOTIFY_STARTDELETE:
1326         TRACE( "start delete %s\n", debugstr_a(paths->Target) );
1327         return FILEOP_DOIT;
1328     case SPFILENOTIFY_ENDDELETE:
1329         TRACE( "end delete %s\n", debugstr_a(paths->Target) );
1330         return 0;
1331     case SPFILENOTIFY_DELETEERROR:
1332         ERR( "delete error %d %s\n", paths->Win32Error, debugstr_a(paths->Target) );
1333         return FILEOP_SKIP;
1334     case SPFILENOTIFY_STARTRENAME:
1335         TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1336         return FILEOP_DOIT;
1337     case SPFILENOTIFY_ENDRENAME:
1338         TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1339         return 0;
1340     case SPFILENOTIFY_RENAMEERROR:
1341         ERR( "rename error %d %s -> %s\n", paths->Win32Error,
1342              debugstr_a(paths->Source), debugstr_a(paths->Target) );
1343         return FILEOP_SKIP;
1344     case SPFILENOTIFY_STARTCOPY:
1345         TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1346         return FILEOP_DOIT;
1347     case SPFILENOTIFY_ENDCOPY:
1348         TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1349         return 0;
1350     case SPFILENOTIFY_COPYERROR:
1351         ERR( "copy error %d %s -> %s\n", paths->Win32Error,
1352              debugstr_a(paths->Source), debugstr_a(paths->Target) );
1353         return FILEOP_SKIP;
1354     case SPFILENOTIFY_NEEDMEDIA:
1355         TRACE( "need media\n" );
1356         return FILEOP_SKIP;
1357     default:
1358         FIXME( "notification %d params %x,%x\n", notification, param1, param2 );
1359         break;
1360     }
1361     return 0;
1362 }
1363
1364
1365 /***********************************************************************
1366  *            SetupDefaultQueueCallbackW   (SETUPAPI.@)
1367  */
1368 UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification,
1369                                         UINT_PTR param1, UINT_PTR param2 )
1370 {
1371     FIXME( "notification %d params %x,%x\n", notification, param1, param2 );
1372     return FILEOP_SKIP;
1373 }