setupapi: Implement pSetupGetField, with 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winreg.h"
26 #include "winternl.h"
27 #include "winerror.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winnls.h"
31 #include "setupapi.h"
32 #include "wine/unicode.h"
33 #include "setupapi_private.h"
34 #include "winver.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
38
39 /* context structure for the default queue callback */
40 struct default_callback_context
41 {
42     HWND owner;
43     HWND progress;
44     UINT message;
45 };
46
47 struct file_op
48 {
49     struct file_op *next;
50     UINT            style;
51     WCHAR          *src_root;
52     WCHAR          *src_path;
53     WCHAR          *src_file;
54     WCHAR          *src_descr;
55     WCHAR          *src_tag;
56     WCHAR          *dst_path;
57     WCHAR          *dst_file;
58 };
59
60 struct file_op_queue
61 {
62     struct file_op *head;
63     struct file_op *tail;
64     unsigned int count;
65 };
66
67 struct file_queue
68 {
69     struct file_op_queue copy_queue;
70     struct file_op_queue delete_queue;
71     struct file_op_queue rename_queue;
72     DWORD                flags;
73 };
74
75
76 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         if ((op->style & (SP_COPY_SOURCE_ABSOLUTE|SP_COPY_SOURCEPATH_ABSOLUTE))) return;
300         /* no specific info, use .inf file source directory */
301         if (!op->src_root) op->src_root = PARSER_get_src_root( hinf );
302         return;
303     }
304     if (!SetupGetIntField( &file_ctx, 1, &diskid )) return;
305
306     /* now find the diskid in the SourceDisksNames section */
307     if (!SetupFindFirstLineW( hinf, SourceDisksNames, NULL, &disk_ctx )) return;
308     for (;;)
309     {
310         if (SetupGetIntField( &disk_ctx, 0, &id ) && (id == diskid)) break;
311         if (!SetupFindNextLine( &disk_ctx, &disk_ctx )) return;
312     }
313
314     /* and fill in the missing info */
315
316     if (!op->src_descr)
317     {
318         if (SetupGetStringFieldW( &disk_ctx, 1, NULL, 0, &len ) &&
319             (op->src_descr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) )))
320             SetupGetStringFieldW( &disk_ctx, 1, op->src_descr, len, NULL );
321     }
322     if (!op->src_tag)
323     {
324         if (SetupGetStringFieldW( &disk_ctx, 2, NULL, 0, &len ) &&
325             (op->src_tag = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) )))
326             SetupGetStringFieldW( &disk_ctx, 2, op->src_tag, len, NULL );
327     }
328     if (!op->src_path && !(op->style & SP_COPY_SOURCE_ABSOLUTE))
329     {
330         if (!(op->style & SP_COPY_SOURCEPATH_ABSOLUTE))
331         {
332             /* retrieve relative path for this disk */
333             if (!SetupGetStringFieldW( &disk_ctx, 4, NULL, 0, &len )) len = 0;
334         }
335         /* retrieve relative path for this file */
336         if (!SetupGetStringFieldW( &file_ctx, 2, NULL, 0, &len2 )) len2 = 0;
337
338         if ((len || len2) &&
339             (op->src_path = HeapAlloc( GetProcessHeap(), 0, (len+len2)*sizeof(WCHAR) )))
340         {
341             WCHAR *ptr = op->src_path;
342             if (len)
343             {
344                 SetupGetStringFieldW( &disk_ctx, 4, op->src_path, len, NULL );
345                 ptr = op->src_path + strlenW(op->src_path);
346                 if (len2 && ptr > op->src_path && ptr[-1] != '\\') *ptr++ = '\\';
347             }
348             if (!SetupGetStringFieldW( &disk_ctx, 4, ptr, len2, NULL )) *ptr = 0;
349         }
350     }
351     if (!op->src_root) op->src_root = PARSER_get_src_root(hinf);
352 }
353
354
355 /***********************************************************************
356  *            get_destination_dir
357  *
358  * Retrieve the destination dir for a given section.
359  */
360 static WCHAR *get_destination_dir( HINF hinf, const WCHAR *section )
361 {
362     static const WCHAR Dest[] = {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0};
363     static const WCHAR Def[]  = {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0};
364     INFCONTEXT context;
365
366     if (!SetupFindFirstLineW( hinf, Dest, section, &context ) &&
367         !SetupFindFirstLineW( hinf, Dest, Def, &context )) return NULL;
368     return PARSER_get_dest_dir( &context );
369 }
370
371
372 static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD );
373
374 /***********************************************************************
375  *            extract_cabinet_file
376  *
377  * Extract a file from a .cab file.
378  */
379 static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root,
380                                   const WCHAR *src, const WCHAR *dst )
381 {
382     static const WCHAR extW[] = {'.','c','a','b',0};
383     static HMODULE advpack;
384
385     char *cab_path, *cab_file;
386     int len = strlenW( cabinet );
387
388     /* make sure the cabinet file has a .cab extension */
389     if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE;
390     if (!pExtractFiles)
391     {
392         if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" )))
393         {
394             ERR( "could not load advpack.dll\n" );
395             return FALSE;
396         }
397         if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles" )))
398         {
399             ERR( "could not find ExtractFiles in advpack.dll\n" );
400             return FALSE;
401         }
402     }
403
404     if (!(cab_path = strdupWtoA( root ))) return FALSE;
405     len = WideCharToMultiByte( CP_ACP, 0, cabinet, -1, NULL, 0, NULL, NULL );
406     if (!(cab_file = HeapAlloc( GetProcessHeap(), 0, strlen(cab_path) + len + 1 )))
407     {
408         HeapFree( GetProcessHeap(), 0, cab_path );
409         return FALSE;
410     }
411     strcpy( cab_file, cab_path );
412     if (cab_file[0] && cab_file[strlen(cab_file)-1] != '\\') strcat( cab_file, "\\" );
413     WideCharToMultiByte( CP_ACP, 0, cabinet, -1, cab_file + strlen(cab_file), len, NULL, NULL );
414     FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) );
415     pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 );
416     HeapFree( GetProcessHeap(), 0, cab_file );
417     HeapFree( GetProcessHeap(), 0, cab_path );
418     return CopyFileW( src, dst, FALSE /*FIXME*/ );
419 }
420
421
422 /***********************************************************************
423  *            SetupOpenFileQueue   (SETUPAPI.@)
424  */
425 HSPFILEQ WINAPI SetupOpenFileQueue(void)
426 {
427     struct file_queue *queue;
428
429     if (!(queue = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*queue))))
430         return (HSPFILEQ)INVALID_HANDLE_VALUE;
431     return queue;
432 }
433
434
435 /***********************************************************************
436  *            SetupCloseFileQueue   (SETUPAPI.@)
437  */
438 BOOL WINAPI SetupCloseFileQueue( HSPFILEQ handle )
439 {
440     struct file_queue *queue = handle;
441
442     free_file_op_queue( &queue->copy_queue );
443     free_file_op_queue( &queue->rename_queue );
444     free_file_op_queue( &queue->delete_queue );
445     HeapFree( GetProcessHeap(), 0, queue );
446     return TRUE;
447 }
448
449
450 /***********************************************************************
451  *            SetupQueueCopyIndirectA   (SETUPAPI.@)
452  */
453 BOOL WINAPI SetupQueueCopyIndirectA( PSP_FILE_COPY_PARAMS_A params )
454 {
455     struct file_queue *queue = params->QueueHandle;
456     struct file_op *op;
457
458     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
459     op->style      = params->CopyStyle;
460     op->src_root   = strdupAtoW( params->SourceRootPath );
461     op->src_path   = strdupAtoW( params->SourcePath );
462     op->src_file   = strdupAtoW( params->SourceFilename );
463     op->src_descr  = strdupAtoW( params->SourceDescription );
464     op->src_tag    = strdupAtoW( params->SourceTagfile );
465     op->dst_path   = strdupAtoW( params->TargetDirectory );
466     op->dst_file   = strdupAtoW( params->TargetFilename );
467
468     /* some defaults */
469     if (!op->src_file) op->src_file = op->dst_file;
470     if (params->LayoutInf)
471     {
472         get_src_file_info( params->LayoutInf, op );
473         if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file );
474     }
475
476     TRACE( "root=%s path=%s file=%s -> dir=%s file=%s  descr=%s tag=%s\n",
477            debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file),
478            debugstr_w(op->dst_path), debugstr_w(op->dst_file),
479            debugstr_w(op->src_descr), debugstr_w(op->src_tag) );
480
481     queue_file_op( &queue->copy_queue, op );
482     return TRUE;
483 }
484
485
486 /***********************************************************************
487  *            SetupQueueCopyIndirectW   (SETUPAPI.@)
488  */
489 BOOL WINAPI SetupQueueCopyIndirectW( PSP_FILE_COPY_PARAMS_W params )
490 {
491     struct file_queue *queue = params->QueueHandle;
492     struct file_op *op;
493
494     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
495     op->style      = params->CopyStyle;
496     op->src_root   = strdupW( params->SourceRootPath );
497     op->src_path   = strdupW( params->SourcePath );
498     op->src_file   = strdupW( params->SourceFilename );
499     op->src_descr  = strdupW( params->SourceDescription );
500     op->src_tag    = strdupW( params->SourceTagfile );
501     op->dst_path   = strdupW( params->TargetDirectory );
502     op->dst_file   = strdupW( params->TargetFilename );
503
504     /* some defaults */
505     if (!op->src_file) op->src_file = op->dst_file;
506     if (params->LayoutInf)
507     {
508         get_src_file_info( params->LayoutInf, op );
509         if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file );
510     }
511
512     TRACE( "root=%s path=%s file=%s -> dir=%s file=%s  descr=%s tag=%s\n",
513            debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file),
514            debugstr_w(op->dst_path), debugstr_w(op->dst_file),
515            debugstr_w(op->src_descr), debugstr_w(op->src_tag) );
516
517     queue_file_op( &queue->copy_queue, op );
518     return TRUE;
519 }
520
521
522 /***********************************************************************
523  *            SetupQueueCopyA   (SETUPAPI.@)
524  */
525 BOOL WINAPI SetupQueueCopyA( HSPFILEQ queue, PCSTR src_root, PCSTR src_path, PCSTR src_file,
526                              PCSTR src_descr, PCSTR src_tag, PCSTR dst_dir, PCSTR dst_file,
527                              DWORD style )
528 {
529     SP_FILE_COPY_PARAMS_A params;
530
531     params.cbSize             = sizeof(params);
532     params.QueueHandle        = queue;
533     params.SourceRootPath     = src_root;
534     params.SourcePath         = src_path;
535     params.SourceFilename     = src_file;
536     params.SourceDescription  = src_descr;
537     params.SourceTagfile      = src_tag;
538     params.TargetDirectory    = dst_dir;
539     params.TargetFilename     = dst_file;
540     params.CopyStyle          = style;
541     params.LayoutInf          = 0;
542     params.SecurityDescriptor = NULL;
543     return SetupQueueCopyIndirectA( &params );
544 }
545
546
547 /***********************************************************************
548  *            SetupQueueCopyW   (SETUPAPI.@)
549  */
550 BOOL WINAPI SetupQueueCopyW( HSPFILEQ queue, PCWSTR src_root, PCWSTR src_path, PCWSTR src_file,
551                              PCWSTR src_descr, PCWSTR src_tag, PCWSTR dst_dir, PCWSTR dst_file,
552                              DWORD style )
553 {
554     SP_FILE_COPY_PARAMS_W params;
555
556     params.cbSize             = sizeof(params);
557     params.QueueHandle        = queue;
558     params.SourceRootPath     = src_root;
559     params.SourcePath         = src_path;
560     params.SourceFilename     = src_file;
561     params.SourceDescription  = src_descr;
562     params.SourceTagfile      = src_tag;
563     params.TargetDirectory    = dst_dir;
564     params.TargetFilename     = dst_file;
565     params.CopyStyle          = style;
566     params.LayoutInf          = 0;
567     params.SecurityDescriptor = NULL;
568     return SetupQueueCopyIndirectW( &params );
569 }
570
571
572 /***********************************************************************
573  *            SetupQueueDefaultCopyA   (SETUPAPI.@)
574  */
575 BOOL WINAPI SetupQueueDefaultCopyA( HSPFILEQ queue, HINF hinf, PCSTR src_root, PCSTR src_file,
576                                     PCSTR dst_file, DWORD style )
577 {
578     SP_FILE_COPY_PARAMS_A params;
579
580     params.cbSize             = sizeof(params);
581     params.QueueHandle        = queue;
582     params.SourceRootPath     = src_root;
583     params.SourcePath         = NULL;
584     params.SourceFilename     = src_file;
585     params.SourceDescription  = NULL;
586     params.SourceTagfile      = NULL;
587     params.TargetDirectory    = NULL;
588     params.TargetFilename     = dst_file;
589     params.CopyStyle          = style;
590     params.LayoutInf          = hinf;
591     params.SecurityDescriptor = NULL;
592     return SetupQueueCopyIndirectA( &params );
593 }
594
595
596 /***********************************************************************
597  *            SetupQueueDefaultCopyW   (SETUPAPI.@)
598  */
599 BOOL WINAPI SetupQueueDefaultCopyW( HSPFILEQ queue, HINF hinf, PCWSTR src_root, PCWSTR src_file,
600                                     PCWSTR dst_file, DWORD style )
601 {
602     SP_FILE_COPY_PARAMS_W params;
603
604     params.cbSize             = sizeof(params);
605     params.QueueHandle        = queue;
606     params.SourceRootPath     = src_root;
607     params.SourcePath         = NULL;
608     params.SourceFilename     = src_file;
609     params.SourceDescription  = NULL;
610     params.SourceTagfile      = NULL;
611     params.TargetDirectory    = NULL;
612     params.TargetFilename     = dst_file;
613     params.CopyStyle          = style;
614     params.LayoutInf          = hinf;
615     params.SecurityDescriptor = NULL;
616     return SetupQueueCopyIndirectW( &params );
617 }
618
619
620 /***********************************************************************
621  *            SetupQueueDeleteA   (SETUPAPI.@)
622  */
623 BOOL WINAPI SetupQueueDeleteA( HSPFILEQ handle, PCSTR part1, PCSTR part2 )
624 {
625     struct file_queue *queue = handle;
626     struct file_op *op;
627
628     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
629     op->style      = 0;
630     op->src_root   = NULL;
631     op->src_path   = NULL;
632     op->src_file   = NULL;
633     op->src_descr  = NULL;
634     op->src_tag    = NULL;
635     op->dst_path   = strdupAtoW( part1 );
636     op->dst_file   = strdupAtoW( part2 );
637     queue_file_op( &queue->delete_queue, op );
638     return TRUE;
639 }
640
641
642 /***********************************************************************
643  *            SetupQueueDeleteW   (SETUPAPI.@)
644  */
645 BOOL WINAPI SetupQueueDeleteW( HSPFILEQ handle, PCWSTR part1, PCWSTR part2 )
646 {
647     struct file_queue *queue = handle;
648     struct file_op *op;
649
650     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
651     op->style      = 0;
652     op->src_root   = NULL;
653     op->src_path   = NULL;
654     op->src_file   = NULL;
655     op->src_descr  = NULL;
656     op->src_tag    = NULL;
657     op->dst_path   = strdupW( part1 );
658     op->dst_file   = strdupW( part2 );
659     queue_file_op( &queue->delete_queue, op );
660     return TRUE;
661 }
662
663
664 /***********************************************************************
665  *            SetupQueueRenameA   (SETUPAPI.@)
666  */
667 BOOL WINAPI SetupQueueRenameA( HSPFILEQ handle, PCSTR SourcePath, PCSTR SourceFilename,
668                                PCSTR TargetPath, PCSTR TargetFilename )
669 {
670     struct file_queue *queue = handle;
671     struct file_op *op;
672
673     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
674     op->style      = 0;
675     op->src_root   = NULL;
676     op->src_path   = strdupAtoW( SourcePath );
677     op->src_file   = strdupAtoW( SourceFilename );
678     op->src_descr  = NULL;
679     op->src_tag    = NULL;
680     op->dst_path   = strdupAtoW( TargetPath );
681     op->dst_file   = strdupAtoW( TargetFilename );
682     queue_file_op( &queue->rename_queue, op );
683     return TRUE;
684 }
685
686
687 /***********************************************************************
688  *            SetupQueueRenameW   (SETUPAPI.@)
689  */
690 BOOL WINAPI SetupQueueRenameW( HSPFILEQ handle, PCWSTR SourcePath, PCWSTR SourceFilename,
691                                PCWSTR TargetPath, PCWSTR TargetFilename )
692 {
693     struct file_queue *queue = handle;
694     struct file_op *op;
695
696     if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
697     op->style      = 0;
698     op->src_root   = NULL;
699     op->src_path   = strdupW( SourcePath );
700     op->src_file   = strdupW( SourceFilename );
701     op->src_descr  = NULL;
702     op->src_tag    = NULL;
703     op->dst_path   = strdupW( TargetPath );
704     op->dst_file   = strdupW( TargetFilename );
705     queue_file_op( &queue->rename_queue, op );
706     return TRUE;
707 }
708
709
710 /***********************************************************************
711  *            SetupQueueCopySectionA   (SETUPAPI.@)
712  */
713 BOOL WINAPI SetupQueueCopySectionA( HSPFILEQ queue, PCSTR src_root, HINF hinf, HINF hlist,
714                                     PCSTR section, DWORD style )
715 {
716     UNICODE_STRING sectionW;
717     BOOL ret = FALSE;
718
719     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
720     {
721         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
722         return FALSE;
723     }
724     if (!src_root)
725         ret = SetupQueueCopySectionW( queue, NULL, hinf, hlist, sectionW.Buffer, style );
726     else
727     {
728         UNICODE_STRING srcW;
729         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
730         {
731             ret = SetupQueueCopySectionW( queue, srcW.Buffer, hinf, hlist, sectionW.Buffer, style );
732             RtlFreeUnicodeString( &srcW );
733         }
734         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
735     }
736     RtlFreeUnicodeString( &sectionW );
737     return ret;
738 }
739
740
741 /***********************************************************************
742  *            SetupQueueCopySectionW   (SETUPAPI.@)
743  */
744 BOOL WINAPI SetupQueueCopySectionW( HSPFILEQ queue, PCWSTR src_root, HINF hinf, HINF hlist,
745                                     PCWSTR section, DWORD style )
746 {
747     SP_FILE_COPY_PARAMS_W params;
748     INFCONTEXT context;
749     WCHAR dest[MAX_PATH], src[MAX_PATH];
750     INT flags;
751
752     TRACE( "hinf=%p/%p section=%s root=%s\n",
753            hinf, hlist, debugstr_w(section), debugstr_w(src_root) );
754
755     params.cbSize             = sizeof(params);
756     params.QueueHandle        = queue;
757     params.SourceRootPath     = src_root;
758     params.SourcePath         = NULL;
759     params.SourceDescription  = NULL;
760     params.SourceTagfile      = NULL;
761     params.TargetFilename     = dest;
762     params.CopyStyle          = style;
763     params.LayoutInf          = hinf;
764     params.SecurityDescriptor = NULL;
765
766     if (!hlist) hlist = hinf;
767     if (!hinf) hinf = hlist;
768     if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
769     if (!(params.TargetDirectory = get_destination_dir( hinf, section ))) return FALSE;
770     do
771     {
772         if (!SetupGetStringFieldW( &context, 1, dest, sizeof(dest)/sizeof(WCHAR), NULL ))
773             return FALSE;
774         if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) *src = 0;
775         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;  /* FIXME */
776
777         params.SourceFilename = *src ? src : NULL;
778         if (!SetupQueueCopyIndirectW( &params )) return FALSE;
779     } while (SetupFindNextLine( &context, &context ));
780     return TRUE;
781 }
782
783
784 /***********************************************************************
785  *            SetupQueueDeleteSectionA   (SETUPAPI.@)
786  */
787 BOOL WINAPI SetupQueueDeleteSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
788 {
789     UNICODE_STRING sectionW;
790     BOOL ret = FALSE;
791
792     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
793     {
794         ret = SetupQueueDeleteSectionW( queue, hinf, hlist, sectionW.Buffer );
795         RtlFreeUnicodeString( &sectionW );
796     }
797     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
798     return ret;
799 }
800
801
802 /***********************************************************************
803  *            SetupQueueDeleteSectionW   (SETUPAPI.@)
804  */
805 BOOL WINAPI SetupQueueDeleteSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
806 {
807     INFCONTEXT context;
808     WCHAR *dest_dir;
809     WCHAR buffer[MAX_PATH];
810     BOOL ret = FALSE;
811     INT flags;
812
813     TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
814
815     if (!hlist) hlist = hinf;
816     if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
817     if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
818     do
819     {
820         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
821             goto done;
822         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
823         if (!SetupQueueDeleteW( queue, dest_dir, buffer )) goto done;
824     } while (SetupFindNextLine( &context, &context ));
825
826     ret = TRUE;
827  done:
828     HeapFree( GetProcessHeap(), 0, dest_dir );
829     return ret;
830 }
831
832
833 /***********************************************************************
834  *            SetupQueueRenameSectionA   (SETUPAPI.@)
835  */
836 BOOL WINAPI SetupQueueRenameSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
837 {
838     UNICODE_STRING sectionW;
839     BOOL ret = FALSE;
840
841     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
842     {
843         ret = SetupQueueRenameSectionW( queue, hinf, hlist, sectionW.Buffer );
844         RtlFreeUnicodeString( &sectionW );
845     }
846     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
847     return ret;
848 }
849
850
851 /***********************************************************************
852  *            SetupQueueRenameSectionW   (SETUPAPI.@)
853  */
854 BOOL WINAPI SetupQueueRenameSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
855 {
856     INFCONTEXT context;
857     WCHAR *dest_dir;
858     WCHAR src[MAX_PATH], dst[MAX_PATH];
859     BOOL ret = FALSE;
860
861     TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
862
863     if (!hlist) hlist = hinf;
864     if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
865     if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
866     do
867     {
868         if (!SetupGetStringFieldW( &context, 1, dst, sizeof(dst)/sizeof(WCHAR), NULL ))
869             goto done;
870         if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL ))
871             goto done;
872         if (!SetupQueueRenameW( queue, dest_dir, src, NULL, dst )) goto done;
873     } while (SetupFindNextLine( &context, &context ));
874
875     ret = TRUE;
876  done:
877     HeapFree( GetProcessHeap(), 0, dest_dir );
878     return ret;
879 }
880
881
882 /***********************************************************************
883  *            SetupCommitFileQueueA   (SETUPAPI.@)
884  */
885 BOOL WINAPI SetupCommitFileQueueA( HWND owner, HSPFILEQ queue, PSP_FILE_CALLBACK_A handler,
886                                    PVOID context )
887 {
888     struct callback_WtoA_context ctx;
889
890     ctx.orig_context = context;
891     ctx.orig_handler = handler;
892     return SetupCommitFileQueueW( owner, queue, QUEUE_callback_WtoA, &ctx );
893 }
894
895
896 /***********************************************************************
897  *            create_full_pathW
898  *
899  * Recursively create all directories in the path.
900  */
901 static BOOL create_full_pathW(const WCHAR *path)
902 {
903     BOOL ret = TRUE;
904     int len;
905     WCHAR *new_path;
906
907     new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR));
908     strcpyW(new_path, path);
909
910     while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
911         new_path[len - 1] = 0;
912
913     while(!CreateDirectoryW(new_path, NULL))
914     {
915         WCHAR *slash;
916         DWORD last_error = GetLastError();
917
918         if(last_error == ERROR_ALREADY_EXISTS)
919             break;
920
921         if(last_error != ERROR_PATH_NOT_FOUND)
922         {
923             ret = FALSE;
924             break;
925         }
926
927         if(!(slash = strrchrW(new_path, '\\')))
928         {
929             ret = FALSE;
930             break;
931         }
932
933         len = slash - new_path;
934         new_path[len] = 0;
935         if(!create_full_pathW(new_path))
936         {
937             ret = FALSE;
938             break;
939         }
940         new_path[len] = '\\';
941     }
942
943     HeapFree(GetProcessHeap(), 0, new_path);
944     return ret;
945 }
946
947 static BOOL do_file_copyW( LPCWSTR source, LPCWSTR target, DWORD style, 
948                            PSP_FILE_CALLBACK_W handler, PVOID context )
949 {
950     BOOL rc = FALSE;
951     BOOL docopy = TRUE;
952
953     TRACE("copy %s to %s style 0x%lx\n",debugstr_w(source),debugstr_w(target),style);
954
955     /* before copy processing */
956     if (style & SP_COPY_REPLACEONLY)
957     {
958         if (GetFileAttributesW(target) == INVALID_FILE_ATTRIBUTES)
959             docopy = FALSE;
960     }
961     if (style & (SP_COPY_NEWER_OR_SAME | SP_COPY_NEWER_ONLY | SP_COPY_FORCE_NEWER))
962     {
963         DWORD VersionSizeSource=0;
964         DWORD VersionSizeTarget=0;
965         DWORD zero=0;
966
967         /*
968          * This is sort of an interesting workaround. You see, calling
969          * GetVersionInfoSize on a builtin dll loads that dll into memory
970          * and we do not properly unload builtin dlls.. so we effectively
971          * lock into memory all the targets we are replacing. This leads
972          * to problems when we try to register the replaced dlls.
973          *
974          * So I will test for the existence of the files first so that
975          * we just basically unconditionally replace the builtin versions.
976          */
977         if ((GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) &&
978             (GetFileAttributesW(source) != INVALID_FILE_ATTRIBUTES))
979         {
980             VersionSizeSource = GetFileVersionInfoSizeW(source,&zero);
981             VersionSizeTarget = GetFileVersionInfoSizeW(target,&zero);
982         }
983
984         TRACE("SizeTarget %li ... SizeSource %li\n",VersionSizeTarget,
985                 VersionSizeSource);
986
987         if (VersionSizeSource && VersionSizeTarget)
988         {
989             LPVOID VersionSource;
990             LPVOID VersionTarget;
991             VS_FIXEDFILEINFO *TargetInfo;
992             VS_FIXEDFILEINFO *SourceInfo;
993             UINT length;
994             WCHAR  SubBlock[2]={'\\',0};
995             DWORD  ret;
996
997             VersionSource = HeapAlloc(GetProcessHeap(),0,VersionSizeSource);
998             VersionTarget = HeapAlloc(GetProcessHeap(),0,VersionSizeTarget);
999
1000             ret = GetFileVersionInfoW(source,0,VersionSizeSource,VersionSource);
1001             if (ret)
1002               ret = GetFileVersionInfoW(target, 0, VersionSizeTarget,
1003                     VersionTarget);
1004
1005             if (ret)
1006             {
1007                 ret = VerQueryValueW(VersionSource, SubBlock,
1008                                     (LPVOID*)&SourceInfo, &length);
1009                 if (ret)
1010                     ret = VerQueryValueW(VersionTarget, SubBlock,
1011                                          (LPVOID*)&TargetInfo, &length);
1012
1013                 if (ret)
1014                 {
1015                     FILEPATHS_W filepaths;
1016
1017                     TRACE("Versions: Source %li.%li target %li.%li\n",
1018                       SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS,
1019                       TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS);
1020
1021                     /* used in case of notification */
1022                     filepaths.Target = target;
1023                     filepaths.Source = source;
1024                     filepaths.Win32Error = 0;
1025                     filepaths.Flags = 0;
1026
1027                     if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS)
1028                     {
1029                         if (handler)
1030                             docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1031                         else
1032                             docopy = FALSE;
1033                     }
1034                     else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS)
1035                              && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS))
1036                     {
1037                         if (handler)
1038                             docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1039                         else
1040                             docopy = FALSE;
1041                     }
1042                     else if ((style & SP_COPY_NEWER_ONLY) &&
1043                         (TargetInfo->dwFileVersionMS ==
1044                          SourceInfo->dwFileVersionMS)
1045                         &&(TargetInfo->dwFileVersionLS ==
1046                         SourceInfo->dwFileVersionLS))
1047                     {
1048                         if (handler)
1049                             docopy = handler (context, SPFILENOTIFY_TARGETNEWER, (UINT_PTR)&filepaths, 0);
1050                         else
1051                             docopy = FALSE;
1052                     }
1053                 }
1054             }
1055             HeapFree(GetProcessHeap(),0,VersionSource);
1056             HeapFree(GetProcessHeap(),0,VersionTarget);
1057         }
1058     }
1059     if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE))
1060     {
1061         if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES)
1062         {
1063             FIXME("Notify user target file exists\n");
1064             docopy = FALSE;
1065         }
1066     }
1067     if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE |
1068                  SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP))
1069     {
1070         ERR("Unsupported style(s) 0x%lx\n",style);
1071     }
1072
1073     if (docopy)
1074     {
1075         rc = CopyFileW(source,target,FALSE);
1076         TRACE("Did copy... rc was %i\n",rc);
1077     }
1078
1079     /* after copy processing */
1080     if (style & SP_COPY_DELETESOURCE)
1081     {
1082        if (rc)
1083             DeleteFileW(source);
1084     }
1085
1086     return rc;
1087 }
1088
1089 /***********************************************************************
1090  *            SetupCommitFileQueueW   (SETUPAPI.@)
1091  */
1092 BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler,
1093                                    PVOID context )
1094 {
1095     struct file_queue *queue = handle;
1096     struct file_op *op;
1097     BOOL result = FALSE;
1098     FILEPATHS_W paths;
1099     UINT op_result;
1100
1101     paths.Source = paths.Target = NULL;
1102
1103     if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count)
1104         return TRUE;  /* nothing to do */
1105
1106     if (!handler( context, SPFILENOTIFY_STARTQUEUE, (UINT_PTR)owner, 0 )) return FALSE;
1107
1108     /* perform deletes */
1109
1110     if (queue->delete_queue.count)
1111     {
1112         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE,
1113                        queue->delete_queue.count ))) goto done;
1114         for (op = queue->delete_queue.head; op; op = op->next)
1115         {
1116             build_filepathsW( op, &paths );
1117             op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE);
1118             if (op_result == FILEOP_ABORT) goto done;
1119             while (op_result == FILEOP_DOIT)
1120             {
1121                 TRACE( "deleting file %s\n", debugstr_w(paths.Target) );
1122                 if (DeleteFileW( paths.Target )) break;  /* success */
1123                 paths.Win32Error = GetLastError();
1124                 op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 );
1125                 if (op_result == FILEOP_ABORT) goto done;
1126             }
1127             handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 );
1128         }
1129         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 );
1130     }
1131
1132     /* perform renames */
1133
1134     if (queue->rename_queue.count)
1135     {
1136         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME,
1137                        queue->rename_queue.count ))) goto done;
1138         for (op = queue->rename_queue.head; op; op = op->next)
1139         {
1140             build_filepathsW( op, &paths );
1141             op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME);
1142             if (op_result == FILEOP_ABORT) goto done;
1143             while (op_result == FILEOP_DOIT)
1144             {
1145                 TRACE( "renaming file %s -> %s\n",
1146                        debugstr_w(paths.Source), debugstr_w(paths.Target) );
1147                 if (MoveFileW( paths.Source, paths.Target )) break;  /* success */
1148                 paths.Win32Error = GetLastError();
1149                 op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 );
1150                 if (op_result == FILEOP_ABORT) goto done;
1151             }
1152             handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 );
1153         }
1154         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 );
1155     }
1156
1157     /* perform copies */
1158
1159     if (queue->copy_queue.count)
1160     {
1161         if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY,
1162                        queue->copy_queue.count ))) goto done;
1163         for (op = queue->copy_queue.head; op; op = op->next)
1164         {
1165             WCHAR newpath[MAX_PATH];
1166
1167             build_filepathsW( op, &paths );
1168             op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY );
1169             if (op_result == FILEOP_ABORT) goto done;
1170             if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT;
1171             while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH)
1172             {
1173                 TRACE( "copying file %s -> %s\n",
1174                        debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ),
1175                        debugstr_w(paths.Target) );
1176                 if (op->dst_path)
1177                 {
1178                     if (!create_full_pathW( op->dst_path ))
1179                     {
1180                         paths.Win32Error = GetLastError();
1181                         op_result = handler( context, SPFILENOTIFY_COPYERROR,
1182                                              (UINT_PTR)&paths, (UINT_PTR)newpath );
1183                         if (op_result == FILEOP_ABORT) goto done;
1184                     }
1185                 }
1186                 if (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source,
1187                                paths.Target, op->style, handler, context )) break;  /* success */
1188                 /* try to extract it from the cabinet file */
1189                 if (op->src_tag)
1190                 {
1191                     if (extract_cabinet_file( op->src_tag, op->src_root,
1192                                               paths.Source, paths.Target )) break;
1193                 }
1194                 paths.Win32Error = GetLastError();
1195                 op_result = handler( context, SPFILENOTIFY_COPYERROR,
1196                                      (UINT_PTR)&paths, (UINT_PTR)newpath );
1197                 if (op_result == FILEOP_ABORT) goto done;
1198             }
1199             handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 );
1200         }
1201         handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 );
1202     }
1203
1204
1205     result = TRUE;
1206
1207  done:
1208     handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 );
1209     HeapFree( GetProcessHeap(), 0, (void *)paths.Source );
1210     HeapFree( GetProcessHeap(), 0, (void *)paths.Target );
1211     return result;
1212 }
1213
1214
1215 /***********************************************************************
1216  *            SetupScanFileQueueA   (SETUPAPI.@)
1217  */
1218 BOOL WINAPI SetupScanFileQueueA( HSPFILEQ queue, DWORD flags, HWND window,
1219                                  PSP_FILE_CALLBACK_A callback, PVOID context, PDWORD result )
1220 {
1221     FIXME("stub\n");
1222     return FALSE;
1223 }
1224
1225
1226 /***********************************************************************
1227  *            SetupScanFileQueueW   (SETUPAPI.@)
1228  */
1229 BOOL WINAPI SetupScanFileQueueW( HSPFILEQ queue, DWORD flags, HWND window,
1230                                  PSP_FILE_CALLBACK_W callback, PVOID context, PDWORD result )
1231 {
1232     FIXME("stub\n");
1233     return FALSE;
1234 }
1235
1236
1237 /***********************************************************************
1238  *            SetupGetFileQueueCount   (SETUPAPI.@)
1239  */
1240 BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result )
1241 {
1242     struct file_queue *queue = handle;
1243
1244     switch(op)
1245     {
1246     case FILEOP_COPY:
1247         *result = queue->copy_queue.count;
1248         return TRUE;
1249     case FILEOP_RENAME:
1250         *result = queue->rename_queue.count;
1251         return TRUE;
1252     case FILEOP_DELETE:
1253         *result = queue->delete_queue.count;
1254         return TRUE;
1255     }
1256     return FALSE;
1257 }
1258
1259
1260 /***********************************************************************
1261  *            SetupGetFileQueueFlags   (SETUPAPI.@)
1262  */
1263 BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags )
1264 {
1265     struct file_queue *queue = handle;
1266     *flags = queue->flags;
1267     return TRUE;
1268 }
1269
1270
1271 /***********************************************************************
1272  *            SetupSetFileQueueFlags   (SETUPAPI.@)
1273  */
1274 BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags )
1275 {
1276     struct file_queue *queue = handle;
1277     queue->flags = (queue->flags & ~mask) | flags;
1278     return TRUE;
1279 }
1280
1281
1282 /***********************************************************************
1283  *   SetupSetFileQueueAlternatePlatformA  (SETUPAPI.@)
1284  */
1285 BOOL WINAPI SetupSetFileQueueAlternatePlatformA(HSPFILEQ handle, PSP_ALTPLATFORM_INFO platform, PCSTR catalogfile)
1286 {
1287     FIXME("(%p, %p, %s) stub!\n", handle, platform, debugstr_a(catalogfile));
1288     return FALSE;
1289 }
1290
1291
1292 /***********************************************************************
1293  *   SetupSetFileQueueAlternatePlatformW  (SETUPAPI.@)
1294  */
1295 BOOL WINAPI SetupSetFileQueueAlternatePlatformW(HSPFILEQ handle, PSP_ALTPLATFORM_INFO platform, PCWSTR catalogfile)
1296 {
1297     FIXME("(%p, %p, %s) stub!\n", handle, platform, debugstr_w(catalogfile));
1298     return FALSE;
1299 }
1300
1301
1302 /***********************************************************************
1303  *            SetupInitDefaultQueueCallback   (SETUPAPI.@)
1304  */
1305 PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner )
1306 {
1307     return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL );
1308 }
1309
1310
1311 /***********************************************************************
1312  *            SetupInitDefaultQueueCallbackEx   (SETUPAPI.@)
1313  */
1314 PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg,
1315                                               DWORD reserved1, PVOID reserved2 )
1316 {
1317     struct default_callback_context *context;
1318
1319     if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) )))
1320     {
1321         context->owner    = owner;
1322         context->progress = progress;
1323         context->message  = msg;
1324     }
1325     return context;
1326 }
1327
1328
1329 /***********************************************************************
1330  *            SetupTermDefaultQueueCallback   (SETUPAPI.@)
1331  */
1332 void WINAPI SetupTermDefaultQueueCallback( PVOID context )
1333 {
1334     HeapFree( GetProcessHeap(), 0, context );
1335 }
1336
1337
1338 /***********************************************************************
1339  *            SetupDefaultQueueCallbackA   (SETUPAPI.@)
1340  */
1341 UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification,
1342                                         UINT_PTR param1, UINT_PTR param2 )
1343 {
1344     FILEPATHS_A *paths = (FILEPATHS_A *)param1;
1345     struct default_callback_context *ctx = (struct default_callback_context *)context;
1346
1347     switch(notification)
1348     {
1349     case SPFILENOTIFY_STARTQUEUE:
1350         TRACE( "start queue\n" );
1351         return TRUE;
1352     case SPFILENOTIFY_ENDQUEUE:
1353         TRACE( "end queue\n" );
1354         return 0;
1355     case SPFILENOTIFY_STARTSUBQUEUE:
1356         TRACE( "start subqueue %d count %d\n", param1, param2 );
1357         return TRUE;
1358     case SPFILENOTIFY_ENDSUBQUEUE:
1359         TRACE( "end subqueue %d\n", param1 );
1360         return 0;
1361     case SPFILENOTIFY_STARTDELETE:
1362         TRACE( "start delete %s\n", debugstr_a(paths->Target) );
1363         return FILEOP_DOIT;
1364     case SPFILENOTIFY_ENDDELETE:
1365         TRACE( "end delete %s\n", debugstr_a(paths->Target) );
1366         return 0;
1367     case SPFILENOTIFY_DELETEERROR:
1368         /*Windows Ignores attempts to delete files / folders which do not exist*/
1369         if ((paths->Win32Error != ERROR_FILE_NOT_FOUND) && (paths->Win32Error != ERROR_PATH_NOT_FOUND))
1370         SetupDeleteErrorA(ctx->owner, NULL, paths->Target, paths->Win32Error, 0);
1371         return FILEOP_SKIP;
1372     case SPFILENOTIFY_STARTRENAME:
1373         TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1374         return FILEOP_DOIT;
1375     case SPFILENOTIFY_ENDRENAME:
1376         TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1377         return 0;
1378     case SPFILENOTIFY_RENAMEERROR:
1379         SetupRenameErrorA(ctx->owner, NULL, paths->Source, paths->Target, paths->Win32Error, 0);
1380         return FILEOP_SKIP;
1381     case SPFILENOTIFY_STARTCOPY:
1382         TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1383         return FILEOP_DOIT;
1384     case SPFILENOTIFY_ENDCOPY:
1385         TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
1386         return 0;
1387     case SPFILENOTIFY_COPYERROR:
1388         ERR( "copy error %d %s -> %s\n", paths->Win32Error,
1389              debugstr_a(paths->Source), debugstr_a(paths->Target) );
1390         return FILEOP_SKIP;
1391     case SPFILENOTIFY_NEEDMEDIA:
1392         TRACE( "need media\n" );
1393         return FILEOP_SKIP;
1394     default:
1395         FIXME( "notification %d params %x,%x\n", notification, param1, param2 );
1396         break;
1397     }
1398     return 0;
1399 }
1400
1401
1402 /***********************************************************************
1403  *            SetupDefaultQueueCallbackW   (SETUPAPI.@)
1404  */
1405 UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification,
1406                                         UINT_PTR param1, UINT_PTR param2 )
1407 {
1408     FILEPATHS_W *paths = (FILEPATHS_W *)param1;
1409     struct default_callback_context *ctx = (struct default_callback_context *)context;
1410
1411     switch(notification)
1412     {
1413     case SPFILENOTIFY_STARTQUEUE:
1414         TRACE( "start queue\n" );
1415         return TRUE;
1416     case SPFILENOTIFY_ENDQUEUE:
1417         TRACE( "end queue\n" );
1418         return 0;
1419     case SPFILENOTIFY_STARTSUBQUEUE:
1420         TRACE( "start subqueue %d count %d\n", param1, param2 );
1421         return TRUE;
1422     case SPFILENOTIFY_ENDSUBQUEUE:
1423         TRACE( "end subqueue %d\n", param1 );
1424         return 0;
1425     case SPFILENOTIFY_STARTDELETE:
1426         TRACE( "start delete %s\n", debugstr_w(paths->Target) );
1427         return FILEOP_DOIT;
1428     case SPFILENOTIFY_ENDDELETE:
1429         TRACE( "end delete %s\n", debugstr_w(paths->Target) );
1430         return 0;
1431     case SPFILENOTIFY_DELETEERROR:
1432         /*Windows Ignores attempts to delete files / folders which do not exist*/
1433         if ((paths->Win32Error != ERROR_FILE_NOT_FOUND) && (paths->Win32Error != ERROR_PATH_NOT_FOUND))
1434             SetupDeleteErrorW(ctx->owner, NULL, paths->Target, paths->Win32Error, 0);
1435         return FILEOP_SKIP;
1436     case SPFILENOTIFY_STARTRENAME:
1437         SetupRenameErrorW(ctx->owner, NULL, paths->Source, paths->Target, paths->Win32Error, 0);
1438         return FILEOP_DOIT;
1439     case SPFILENOTIFY_ENDRENAME:
1440         TRACE( "end rename %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1441         return 0;
1442     case SPFILENOTIFY_RENAMEERROR:
1443         ERR( "rename error %d %s -> %s\n", paths->Win32Error,
1444              debugstr_w(paths->Source), debugstr_w(paths->Target) );
1445         return FILEOP_SKIP;
1446     case SPFILENOTIFY_STARTCOPY:
1447         TRACE( "start copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1448         return FILEOP_DOIT;
1449     case SPFILENOTIFY_ENDCOPY:
1450         TRACE( "end copy %s -> %s\n", debugstr_w(paths->Source), debugstr_w(paths->Target) );
1451         return 0;
1452     case SPFILENOTIFY_COPYERROR:
1453         ERR( "copy error %d %s -> %s\n", paths->Win32Error,
1454              debugstr_w(paths->Source), debugstr_w(paths->Target) );
1455         return FILEOP_SKIP;
1456     case SPFILENOTIFY_NEEDMEDIA:
1457         TRACE( "need media\n" );
1458         return FILEOP_SKIP;
1459     default:
1460         FIXME( "notification %d params %x,%x\n", notification, param1, param2 );
1461         break;
1462     }
1463     return 0;
1464 }
1465
1466 /***********************************************************************
1467  *            SetupDeleteErrorA   (SETUPAPI.@)
1468  */
1469
1470 UINT WINAPI SetupDeleteErrorA( HWND parent, PCSTR dialogTitle, PCSTR file,
1471                                UINT w32error, DWORD style)
1472 {
1473     FIXME( "stub: (Error Number %d when attempting to delete %s)\n",
1474            w32error, debugstr_a(file) );
1475     return DPROMPT_SKIPFILE;
1476 }
1477
1478 /***********************************************************************
1479  *            SetupDeleteErrorW   (SETUPAPI.@)
1480  */
1481
1482 UINT WINAPI SetupDeleteErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR file,
1483                                UINT w32error, DWORD style)
1484 {
1485     FIXME( "stub: (Error Number %d when attempting to delete %s)\n",
1486            w32error, debugstr_w(file) );
1487     return DPROMPT_SKIPFILE;
1488 }
1489
1490 /***********************************************************************
1491  *            SetupRenameErrorA   (SETUPAPI.@)
1492  */
1493
1494 UINT WINAPI SetupRenameErrorA( HWND parent, PCSTR dialogTitle, PCSTR source,
1495                                PCSTR target, UINT w32error, DWORD style)
1496 {
1497     FIXME( "stub: (Error Number %d when attempting to rename %s to %s)\n",
1498            w32error, debugstr_a(source), debugstr_a(target));
1499     return DPROMPT_SKIPFILE;
1500 }
1501
1502 /***********************************************************************
1503  *            SetupRenameErrorW   (SETUPAPI.@)
1504  */
1505
1506 UINT WINAPI SetupRenameErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR source,
1507                                PCWSTR target, UINT w32error, DWORD style)
1508 {
1509     FIXME( "stub: (Error Number %d when attempting to rename %s to %s)\n",
1510            w32error, debugstr_w(source), debugstr_w(target));
1511     return DPROMPT_SKIPFILE;
1512 }
1513
1514
1515 /***********************************************************************
1516  *            SetupCopyErrorA   (SETUPAPI.@)
1517  */
1518
1519 UINT WINAPI SetupCopyErrorA( HWND parent, PCSTR dialogTitle, PCSTR diskname, 
1520                              PCSTR sourcepath, PCSTR sourcefile, PCSTR targetpath,
1521                              UINT w32error, DWORD style, PSTR pathbuffer, 
1522                              DWORD buffersize, PDWORD requiredsize)
1523 {
1524     FIXME( "stub: (Error Number %d when attempting to copy file %s from %s to %s)\n",
1525            w32error, debugstr_a(sourcefile), debugstr_a(sourcepath) ,debugstr_a(targetpath));
1526     return DPROMPT_SKIPFILE;
1527 }
1528
1529 /***********************************************************************
1530  *            SetupCopyErrorW   (SETUPAPI.@)
1531  */
1532
1533 UINT WINAPI SetupCopyErrorW( HWND parent, PCWSTR dialogTitle, PCWSTR diskname, 
1534                              PCWSTR sourcepath, PCWSTR sourcefile, PCWSTR targetpath,
1535                              UINT w32error, DWORD style, PWSTR pathbuffer, 
1536                              DWORD buffersize, PDWORD requiredsize)
1537 {
1538     FIXME( "stub: (Error Number %d when attempting to copy file %s from %s to %s)\n",
1539            w32error, debugstr_w(sourcefile), debugstr_w(sourcepath) ,debugstr_w(targetpath));
1540     return DPROMPT_SKIPFILE;
1541 }