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