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