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