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