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