Unconditionally open a fresh console for the tests.
[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 (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
771     if (!(params.TargetDirectory = get_destination_dir( hinf, section ))) return FALSE;
772     do
773     {
774         if (!SetupGetStringFieldW( &context, 1, dest, sizeof(dest)/sizeof(WCHAR), NULL ))
775             return FALSE;
776         if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) *src = 0;
777         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;  /* FIXME */
778
779         params.SourceFilename = *src ? src : NULL;
780         if (!SetupQueueCopyIndirectW( &params )) return FALSE;
781     } while (SetupFindNextLine( &context, &context ));
782     return TRUE;
783 }
784
785
786 /***********************************************************************
787  *            SetupQueueDeleteSectionA   (SETUPAPI.@)
788  */
789 BOOL WINAPI SetupQueueDeleteSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
790 {
791     UNICODE_STRING sectionW;
792     BOOL ret = FALSE;
793
794     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
795     {
796         ret = SetupQueueDeleteSectionW( queue, hinf, hlist, sectionW.Buffer );
797         RtlFreeUnicodeString( &sectionW );
798     }
799     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
800     return ret;
801 }
802
803
804 /***********************************************************************
805  *            SetupQueueDeleteSectionW   (SETUPAPI.@)
806  */
807 BOOL WINAPI SetupQueueDeleteSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
808 {
809     INFCONTEXT context;
810     WCHAR *dest_dir;
811     WCHAR buffer[MAX_PATH];
812     BOOL ret = FALSE;
813     INT flags;
814
815     TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
816
817     if (!hlist) hlist = hinf;
818     if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
819     if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
820     do
821     {
822         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
823             goto done;
824         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
825         if (!SetupQueueDeleteW( queue, dest_dir, buffer )) goto done;
826     } while (SetupFindNextLine( &context, &context ));
827
828     ret = TRUE;
829  done:
830     HeapFree( GetProcessHeap(), 0, dest_dir );
831     return ret;
832 }
833
834
835 /***********************************************************************
836  *            SetupQueueRenameSectionA   (SETUPAPI.@)
837  */
838 BOOL WINAPI SetupQueueRenameSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
839 {
840     UNICODE_STRING sectionW;
841     BOOL ret = FALSE;
842
843     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
844     {
845         ret = SetupQueueRenameSectionW( queue, hinf, hlist, sectionW.Buffer );
846         RtlFreeUnicodeString( &sectionW );
847     }
848     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
849     return ret;
850 }
851
852
853 /***********************************************************************
854  *            SetupQueueRenameSectionW   (SETUPAPI.@)
855  */
856 BOOL WINAPI SetupQueueRenameSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
857 {
858     INFCONTEXT context;
859     WCHAR *dest_dir;
860     WCHAR src[MAX_PATH], dst[MAX_PATH];
861     BOOL ret = FALSE;
862
863     TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
864
865     if (!hlist) hlist = hinf;
866     if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
867     if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
868     do
869     {
870         if (!SetupGetStringFieldW( &context, 1, dst, sizeof(dst)/sizeof(WCHAR), NULL ))
871             goto done;
872         if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL ))
873             goto done;
874         if (!SetupQueueRenameW( queue, dest_dir, src, NULL, dst )) goto done;
875     } while (SetupFindNextLine( &context, &context ));
876
877     ret = TRUE;
878  done:
879     HeapFree( GetProcessHeap(), 0, dest_dir );
880     return ret;
881 }
882
883
884 /***********************************************************************
885  *            SetupCommitFileQueueA   (SETUPAPI.@)
886  */
887 BOOL WINAPI SetupCommitFileQueueA( HWND owner, HSPFILEQ queue, PSP_FILE_CALLBACK_A handler,
888                                    PVOID context )
889 {
890     struct callback_WtoA_context ctx;
891
892     ctx.orig_context = context;
893     ctx.orig_handler = handler;
894     return SetupCommitFileQueueW( owner, queue, QUEUE_callback_WtoA, &ctx );
895 }
896
897
898 /***********************************************************************
899  *            create_full_pathW
900  *
901  * Recursively create all directories in the path.
902  */
903 static BOOL create_full_pathW(const WCHAR *path)
904 {
905     BOOL ret = TRUE;
906     int len;
907     WCHAR *new_path;
908
909     new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR));
910     strcpyW(new_path, path);
911
912     while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
913         new_path[len - 1] = 0;
914
915     while(!CreateDirectoryW(new_path, NULL))
916     {
917         WCHAR *slash;
918         DWORD last_error = GetLastError();
919
920         if(last_error == ERROR_ALREADY_EXISTS)
921             break;
922
923         if(last_error != ERROR_PATH_NOT_FOUND)
924         {
925             ret = FALSE;
926             break;
927         }
928
929         if(!(slash = strrchrW(new_path, '\\')))
930         {
931             ret = FALSE;
932             break;
933         }
934
935         len = slash - new_path;
936         new_path[len] = 0;
937         if(!create_full_pathW(new_path))
938         {
939             ret = FALSE;
940             break;
941         }
942         new_path[len] = '\\';
943     }
944
945     HeapFree(GetProcessHeap(), 0, new_path);
946     return ret;
947 }
948
949 BOOL static do_file_copyW( LPCWSTR source, LPCWSTR target, DWORD style)
950 {
951     BOOL rc = FALSE;
952     BOOL docopy = TRUE;
953
954     TRACE("copy %s to %s style 0x%lx\n",debugstr_w(source),debugstr_w(target),style);
955
956     /* before copy processing */
957     if (style & SP_COPY_REPLACEONLY)
958     {
959         if (GetFileAttributesW(target) == INVALID_FILE_ATTRIBUTES)
960             docopy = FALSE;
961     }
962     if (style & (SP_COPY_NEWER_OR_SAME | SP_COPY_NEWER_ONLY | SP_COPY_FORCE_NEWER))
963     {
964         DWORD VersionSizeSource=0;
965         DWORD VersionSizeTarget=0;
966         DWORD zero=0;
967
968         /*
969          * This is sort of an interesting workaround. You see, calling
970          * GetVersionInfoSize on a builtin dll loads that dll into memory
971          * and we do not properly unload builtin dlls.. so we effectively
972          * lock into memory all the targets we are replacing. This leads
973          * to problems when we try to register the replaced dlls.
974          *
975          * So I will test for the existence of the files first so that
976          * we just basically unconditionally replace the builtin versions.
977          */
978         if ((GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) &&
979             (GetFileAttributesW(source) != INVALID_FILE_ATTRIBUTES))
980         {
981             VersionSizeSource = GetFileVersionInfoSizeW(source,&zero);
982             VersionSizeTarget = GetFileVersionInfoSizeW(target,&zero);
983         }
984
985         TRACE("SizeTarget %li ... SizeSource %li\n",VersionSizeTarget,
986                 VersionSizeSource);
987
988         if (VersionSizeSource && VersionSizeTarget)
989         {
990             LPVOID VersionSource;
991             LPVOID VersionTarget;
992             VS_FIXEDFILEINFO *TargetInfo;
993             VS_FIXEDFILEINFO *SourceInfo;
994             INT length;
995             WCHAR  SubBlock[2]={'\\',0};
996             DWORD  ret;
997
998             VersionSource = HeapAlloc(GetProcessHeap(),0,VersionSizeSource);
999             VersionTarget = HeapAlloc(GetProcessHeap(),0,VersionSizeTarget);
1000
1001             ret = GetFileVersionInfoW(source,0,VersionSizeSource,VersionSource);
1002             if (ret)
1003               ret = GetFileVersionInfoW(target, 0, VersionSizeTarget,
1004                     VersionTarget);
1005
1006             if (ret)
1007             {
1008                 ret = VerQueryValueW(VersionSource, SubBlock,
1009                                     (LPVOID*)&SourceInfo, &length);
1010                 if (ret)
1011                     ret = VerQueryValueW(VersionTarget, SubBlock,
1012                                          (LPVOID*)&TargetInfo, &length);
1013
1014                 if (ret)
1015                 {
1016                     TRACE("Versions: Source %li.%li target %li.%li\n",
1017                       SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS,
1018                       TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS);
1019
1020                     if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS)
1021                     {
1022                         FIXME("Notify that target version is greater..\n");
1023                         docopy = FALSE;
1024                     }
1025                     else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS)
1026                              && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS))
1027                     {
1028                         FIXME("Notify that target version is greater..\n");
1029                         docopy = FALSE;
1030                     }
1031                     else if ((style & SP_COPY_NEWER_ONLY) &&
1032                         (TargetInfo->dwFileVersionMS ==
1033                          SourceInfo->dwFileVersionMS)
1034                         &&(TargetInfo->dwFileVersionLS ==
1035                         SourceInfo->dwFileVersionLS))
1036                     {
1037                         FIXME("Notify that target version is greater..\n");
1038                         docopy = FALSE;
1039                     }
1040                 }
1041             }
1042             HeapFree(GetProcessHeap(),0,VersionSource);
1043             HeapFree(GetProcessHeap(),0,VersionTarget);
1044         }
1045     }
1046     if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE))
1047     {
1048         if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES)
1049         {
1050             FIXME("Notify user target file exists\n");
1051             docopy = FALSE;
1052         }
1053     }
1054     if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE |
1055                  SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP))
1056     {
1057         ERR("Unsupported style(s) 0x%lx\n",style);
1058     }
1059
1060     if (docopy)
1061     {
1062         rc = CopyFileW(source,target,FALSE);
1063         TRACE("Did copy... rc was %i\n",rc);
1064     }
1065
1066     /* after copy processing */
1067     if (style & SP_COPY_DELETESOURCE)
1068     {
1069        if (rc)
1070             DeleteFileW(source);
1071     }
1072
1073     return rc;
1074 }
1075
1076 /***********************************************************************
1077  *            SetupCommitFileQueueW   (SETUPAPI.@)
1078  */
1079 BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler,
1080                                    PVOID context )
1081 {
1082     struct file_queue *queue = handle;
1083     struct file_op *op;
1084     BOOL result = FALSE;
1085     FILEPATHS_W paths;
1086     UINT op_result;
1087
1088     paths.Source = paths.Target = NULL;
1089
1090     if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count)
1091         return TRUE;  /* nothing to do */
1092
1093     if (!handler( context, SPFILENOTIFY_STARTQUEUE, (UINT)owner, 0 )) return FALSE;
1094
1095     /* perform deletes */
1096
1097     if (queue->delete_queue.count)
1098     {
1099         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE,
1100                        queue->delete_queue.count ))) goto done;
1101         for (op = queue->delete_queue.head; op; op = op->next)
1102         {
1103             build_filepathsW( op, &paths );
1104             op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE);
1105             if (op_result == FILEOP_ABORT) goto done;
1106             while (op_result == FILEOP_DOIT)
1107             {
1108                 TRACE( "deleting file %s\n", debugstr_w(paths.Target) );
1109                 if (DeleteFileW( paths.Target )) break;  /* success */
1110                 paths.Win32Error = GetLastError();
1111                 op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 );
1112                 if (op_result == FILEOP_ABORT) goto done;
1113             }
1114             handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 );
1115         }
1116         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 );
1117     }
1118
1119     /* perform renames */
1120
1121     if (queue->rename_queue.count)
1122     {
1123         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME,
1124                        queue->rename_queue.count ))) goto done;
1125         for (op = queue->rename_queue.head; op; op = op->next)
1126         {
1127             build_filepathsW( op, &paths );
1128             op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME);
1129             if (op_result == FILEOP_ABORT) goto done;
1130             while (op_result == FILEOP_DOIT)
1131             {
1132                 TRACE( "renaming file %s -> %s\n",
1133                        debugstr_w(paths.Source), debugstr_w(paths.Target) );
1134                 if (MoveFileW( paths.Source, paths.Target )) break;  /* success */
1135                 paths.Win32Error = GetLastError();
1136                 op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 );
1137                 if (op_result == FILEOP_ABORT) goto done;
1138             }
1139             handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 );
1140         }
1141         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 );
1142     }
1143
1144     /* perform copies */
1145
1146     if (queue->copy_queue.count)
1147     {
1148         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY,
1149                        queue->copy_queue.count ))) goto done;
1150         for (op = queue->copy_queue.head; op; op = op->next)
1151         {
1152             WCHAR newpath[MAX_PATH];
1153
1154             build_filepathsW( op, &paths );
1155             op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY );
1156             if (op_result == FILEOP_ABORT) goto done;
1157             if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT;
1158             while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH)
1159             {
1160                 TRACE( "copying file %s -> %s\n",
1161                        debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ),
1162                        debugstr_w(paths.Target) );
1163                 if (op->dst_path)
1164                 {
1165                     if (!create_full_pathW( op->dst_path ))
1166                     {
1167                         paths.Win32Error = GetLastError();
1168                         op_result = handler( context, SPFILENOTIFY_COPYERROR,
1169                                              (UINT_PTR)&paths, (UINT_PTR)newpath );
1170                         if (op_result == FILEOP_ABORT) goto done;
1171                     }
1172                 }
1173                 if (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source,
1174                                paths.Target, op->style )) break;  /* success */
1175                 /* try to extract it from the cabinet file */
1176                 if (op->src_tag)
1177                 {
1178                     if (extract_cabinet_file( op->src_tag, op->src_root,
1179                                               paths.Source, paths.Target )) break;
1180                 }
1181                 paths.Win32Error = GetLastError();
1182                 op_result = handler( context, SPFILENOTIFY_COPYERROR,
1183                                      (UINT_PTR)&paths, (UINT_PTR)newpath );
1184                 if (op_result == FILEOP_ABORT) goto done;
1185             }
1186             handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 );
1187         }
1188         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 );
1189     }
1190
1191
1192     result = TRUE;
1193
1194  done:
1195     handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 );
1196     HeapFree( GetProcessHeap(), 0, (void *)paths.Source );
1197     HeapFree( GetProcessHeap(), 0, (void *)paths.Target );
1198     return result;
1199 }
1200
1201
1202 /***********************************************************************
1203  *            SetupScanFileQueueA   (SETUPAPI.@)
1204  */
1205 BOOL WINAPI SetupScanFileQueueA( HSPFILEQ queue, DWORD flags, HWND window,
1206                                  PSP_FILE_CALLBACK_A callback, PVOID context, PDWORD result )
1207 {
1208     FIXME("stub\n");
1209     return FALSE;
1210 }
1211
1212
1213 /***********************************************************************
1214  *            SetupScanFileQueueW   (SETUPAPI.@)
1215  */
1216 BOOL WINAPI SetupScanFileQueueW( HSPFILEQ queue, DWORD flags, HWND window,
1217                                  PSP_FILE_CALLBACK_W callback, PVOID context, PDWORD result )
1218 {
1219     FIXME("stub\n");
1220     return FALSE;
1221 }
1222
1223
1224 /***********************************************************************
1225  *            SetupGetFileQueueCount   (SETUPAPI.@)
1226  */
1227 BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result )
1228 {
1229     struct file_queue *queue = handle;
1230
1231     switch(op)
1232     {
1233     case FILEOP_COPY:
1234         *result = queue->copy_queue.count;
1235         return TRUE;
1236     case FILEOP_RENAME:
1237         *result = queue->rename_queue.count;
1238         return TRUE;
1239     case FILEOP_DELETE:
1240         *result = queue->delete_queue.count;
1241         return TRUE;
1242     }
1243     return FALSE;
1244 }
1245
1246
1247 /***********************************************************************
1248  *            SetupGetFileQueueFlags   (SETUPAPI.@)
1249  */
1250 BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags )
1251 {
1252     struct file_queue *queue = handle;
1253     *flags = queue->flags;
1254     return TRUE;
1255 }
1256
1257
1258 /***********************************************************************
1259  *            SetupSetFileQueueFlags   (SETUPAPI.@)
1260  */
1261 BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags )
1262 {
1263     struct file_queue *queue = handle;
1264     queue->flags = (queue->flags & ~mask) | flags;
1265     return TRUE;
1266 }
1267
1268
1269 /***********************************************************************
1270  *            SetupInitDefaultQueueCallback   (SETUPAPI.@)
1271  */
1272 PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner )
1273 {
1274     return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL );
1275 }
1276
1277
1278 /***********************************************************************
1279  *            SetupInitDefaultQueueCallbackEx   (SETUPAPI.@)
1280  */
1281 PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg,
1282                                               DWORD reserved1, PVOID reserved2 )
1283 {
1284     struct default_callback_context *context;
1285
1286     if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) )))
1287     {
1288         context->owner    = owner;
1289         context->progress = progress;
1290         context->message  = msg;
1291     }
1292     return context;
1293 }
1294
1295
1296 /***********************************************************************
1297  *            SetupTermDefaultQueueCallback   (SETUPAPI.@)
1298  */
1299 void WINAPI SetupTermDefaultQueueCallback( PVOID context )
1300 {
1301     HeapFree( GetProcessHeap(), 0, context );
1302 }
1303
1304
1305 /***********************************************************************
1306  *            SetupDefaultQueueCallbackA   (SETUPAPI.@)
1307  */
1308 UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification,
1309                                         UINT_PTR param1, UINT_PTR param2 )
1310 {
1311     FILEPATHS_A *paths = (FILEPATHS_A *)param1;
1312
1313     switch(notification)
1314     {
1315     case SPFILENOTIFY_STARTQUEUE:
1316         TRACE( "start queue\n" );
1317         return TRUE;
1318     case SPFILENOTIFY_ENDQUEUE:
1319         TRACE( "end queue\n" );
1320         return 0;
1321     case SPFILENOTIFY_STARTSUBQUEUE:
1322         TRACE( "start subqueue %d count %d\n", param1, param2 );
1323         return TRUE;
1324     case SPFILENOTIFY_ENDSUBQUEUE:
1325         TRACE( "end subqueue %d\n", param1 );
1326         return 0;
1327     case SPFILENOTIFY_STARTDELETE:
1328         TRACE( "start delete %s\n", debugstr_a(paths->Target) );
1329         return FILEOP_DOIT;
1330     case SPFILENOTIFY_ENDDELETE:
1331         TRACE( "end delete %s\n", debugstr_a(paths->Target) );
1332         return 0;
1333     case SPFILENOTIFY_DELETEERROR:
1334         ERR( "delete error %d %s\n", paths->Win32Error, debugstr_a(paths->Target) );
1335         return FILEOP_SKIP;
1336     case SPFILENOTIFY_STARTRENAME:
1337         TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1338         return FILEOP_DOIT;
1339     case SPFILENOTIFY_ENDRENAME:
1340         TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1341         return 0;
1342     case SPFILENOTIFY_RENAMEERROR:
1343         ERR( "rename error %d %s -> %s\n", paths->Win32Error,
1344              debugstr_a(paths->Source), debugstr_a(paths->Target) );
1345         return FILEOP_SKIP;
1346     case SPFILENOTIFY_STARTCOPY:
1347         TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1348         return FILEOP_DOIT;
1349     case SPFILENOTIFY_ENDCOPY:
1350         TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1351         return 0;
1352     case SPFILENOTIFY_COPYERROR:
1353         ERR( "copy error %d %s -> %s\n", paths->Win32Error,
1354              debugstr_a(paths->Source), debugstr_a(paths->Target) );
1355         return FILEOP_SKIP;
1356     case SPFILENOTIFY_NEEDMEDIA:
1357         TRACE( "need media\n" );
1358         return FILEOP_SKIP;
1359     default:
1360         FIXME( "notification %d params %x,%x\n", notification, param1, param2 );
1361         break;
1362     }
1363     return 0;
1364 }
1365
1366
1367 /***********************************************************************
1368  *            SetupDefaultQueueCallbackW   (SETUPAPI.@)
1369  */
1370 UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification,
1371                                         UINT_PTR param1, UINT_PTR param2 )
1372 {
1373     FIXME( "notification %d params %x,%x\n", notification, param1, param2 );
1374     return FILEOP_SKIP;
1375 }