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