hal: Added an initial stub.
[wine] / dlls / setupapi / install.c
1 /*
2  * Setupapi install routines
3  *
4  * Copyright 2002 Alexandre Julliard for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winreg.h"
26 #include "winternl.h"
27 #include "winerror.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winnls.h"
31 #include "setupapi.h"
32 #include "setupapi_private.h"
33 #include "wine/unicode.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
37
38 /* info passed to callback functions dealing with files */
39 struct files_callback_info
40 {
41     HSPFILEQ queue;
42     PCWSTR   src_root;
43     UINT     copy_flags;
44     HINF     layout;
45 };
46
47 /* info passed to callback functions dealing with the registry */
48 struct registry_callback_info
49 {
50     HKEY default_root;
51     BOOL delete;
52 };
53
54 /* info passed to callback functions dealing with registering dlls */
55 struct register_dll_info
56 {
57     PSP_FILE_CALLBACK_W callback;
58     PVOID               callback_context;
59     BOOL                unregister;
60 };
61
62 typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
63
64 /* Unicode constants */
65 static const WCHAR CopyFiles[]  = {'C','o','p','y','F','i','l','e','s',0};
66 static const WCHAR DelFiles[]   = {'D','e','l','F','i','l','e','s',0};
67 static const WCHAR RenFiles[]   = {'R','e','n','F','i','l','e','s',0};
68 static const WCHAR Ini2Reg[]    = {'I','n','i','2','R','e','g',0};
69 static const WCHAR LogConf[]    = {'L','o','g','C','o','n','f',0};
70 static const WCHAR AddReg[]     = {'A','d','d','R','e','g',0};
71 static const WCHAR DelReg[]     = {'D','e','l','R','e','g',0};
72 static const WCHAR BitReg[]     = {'B','i','t','R','e','g',0};
73 static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
74 static const WCHAR CopyINF[]    = {'C','o','p','y','I','N','F',0};
75 static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
76 static const WCHAR RegisterDlls[]    = {'R','e','g','i','s','t','e','r','D','l','l','s',0};
77 static const WCHAR UnregisterDlls[]  = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0};
78 static const WCHAR ProfileItems[]    = {'P','r','o','f','i','l','e','I','t','e','m','s',0};
79 static const WCHAR WineFakeDlls[]    = {'W','i','n','e','F','a','k','e','D','l','l','s',0};
80
81
82 /***********************************************************************
83  *            get_field_string
84  *
85  * Retrieve the contents of a field, dynamically growing the buffer if necessary.
86  */
87 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
88                                 WCHAR *static_buffer, DWORD *size )
89 {
90     DWORD required;
91
92     if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
93     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
94     {
95         /* now grow the buffer */
96         if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
97         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
98         *size = required;
99         if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
100     }
101     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
102     return NULL;
103 }
104
105
106 /***********************************************************************
107  *            copy_files_callback
108  *
109  * Called once for each CopyFiles entry in a given section.
110  */
111 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
112 {
113     struct files_callback_info *info = arg;
114
115     if (field[0] == '@')  /* special case: copy single file */
116         SetupQueueDefaultCopyW( info->queue, info->layout ? info->layout : hinf, info->src_root, NULL, field+1, info->copy_flags );
117     else
118         SetupQueueCopySectionW( info->queue, info->src_root, info->layout ? info->layout : hinf, hinf, field, info->copy_flags );
119     return TRUE;
120 }
121
122
123 /***********************************************************************
124  *            delete_files_callback
125  *
126  * Called once for each DelFiles entry in a given section.
127  */
128 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
129 {
130     struct files_callback_info *info = arg;
131     SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
132     return TRUE;
133 }
134
135
136 /***********************************************************************
137  *            rename_files_callback
138  *
139  * Called once for each RenFiles entry in a given section.
140  */
141 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
142 {
143     struct files_callback_info *info = arg;
144     SetupQueueRenameSectionW( info->queue, hinf, 0, field );
145     return TRUE;
146 }
147
148
149 /***********************************************************************
150  *            get_root_key
151  *
152  * Retrieve the registry root key from its name.
153  */
154 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
155 {
156     static const WCHAR HKCR[] = {'H','K','C','R',0};
157     static const WCHAR HKCU[] = {'H','K','C','U',0};
158     static const WCHAR HKLM[] = {'H','K','L','M',0};
159     static const WCHAR HKU[]  = {'H','K','U',0};
160     static const WCHAR HKR[]  = {'H','K','R',0};
161
162     if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
163     if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
164     if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
165     if (!strcmpiW( name, HKU )) return HKEY_USERS;
166     if (!strcmpiW( name, HKR )) return def_root;
167     return 0;
168 }
169
170
171 /***********************************************************************
172  *            append_multi_sz_value
173  *
174  * Append a multisz string to a multisz registry value.
175  */
176 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
177                                    DWORD str_size )
178 {
179     DWORD size, type, total;
180     WCHAR *buffer, *p;
181
182     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
183     if (type != REG_MULTI_SZ) return;
184
185     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
186     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
187
188     /* compare each string against all the existing ones */
189     total = size;
190     while (*strings)
191     {
192         int len = strlenW(strings) + 1;
193
194         for (p = buffer; *p; p += strlenW(p) + 1)
195             if (!strcmpiW( p, strings )) break;
196
197         if (!*p)  /* not found, need to append it */
198         {
199             memcpy( p, strings, len * sizeof(WCHAR) );
200             p[len] = 0;
201             total += len;
202         }
203         strings += len;
204     }
205     if (total != size)
206     {
207         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
208         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
209     }
210  done:
211     HeapFree( GetProcessHeap(), 0, buffer );
212 }
213
214
215 /***********************************************************************
216  *            delete_multi_sz_value
217  *
218  * Remove a string from a multisz registry value.
219  */
220 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
221 {
222     DWORD size, type;
223     WCHAR *buffer, *src, *dst;
224
225     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
226     if (type != REG_MULTI_SZ) return;
227     /* allocate double the size, one for value before and one for after */
228     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
229     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
230     src = buffer;
231     dst = buffer + size;
232     while (*src)
233     {
234         int len = strlenW(src) + 1;
235         if (strcmpiW( src, string ))
236         {
237             memcpy( dst, src, len * sizeof(WCHAR) );
238             dst += len;
239         }
240         src += len;
241     }
242     *dst++ = 0;
243     if (dst != buffer + 2*size)  /* did we remove something? */
244     {
245         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
246         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
247                         (BYTE *)(buffer + size), dst - (buffer + size) );
248     }
249  done:
250     HeapFree( GetProcessHeap(), 0, buffer );
251 }
252
253
254 /***********************************************************************
255  *            do_reg_operation
256  *
257  * Perform an add/delete registry operation depending on the flags.
258  */
259 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
260 {
261     DWORD type, size;
262
263     if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL))  /* deletion */
264     {
265         if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
266         {
267             if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
268             {
269                 WCHAR *str;
270
271                 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
272                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
273                 SetupGetStringFieldW( context, 5, str, size, NULL );
274                 delete_multi_sz_value( hkey, value, str );
275                 HeapFree( GetProcessHeap(), 0, str );
276             }
277             else RegDeleteValueW( hkey, value );
278         }
279         else RegDeleteKeyW( hkey, NULL );
280         return TRUE;
281     }
282
283     if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
284
285     if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
286     {
287         BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
288         if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
289         if (!exists && (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
290     }
291
292     switch(flags & FLG_ADDREG_TYPE_MASK)
293     {
294     case FLG_ADDREG_TYPE_SZ:        type = REG_SZ; break;
295     case FLG_ADDREG_TYPE_MULTI_SZ:  type = REG_MULTI_SZ; break;
296     case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
297     case FLG_ADDREG_TYPE_BINARY:    type = REG_BINARY; break;
298     case FLG_ADDREG_TYPE_DWORD:     type = REG_DWORD; break;
299     case FLG_ADDREG_TYPE_NONE:      type = REG_NONE; break;
300     default:                        type = flags >> 16; break;
301     }
302
303     if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
304         (type == REG_DWORD && SetupGetFieldCount(context) == 5))
305     {
306         static const WCHAR empty;
307         WCHAR *str = NULL;
308
309         if (type == REG_MULTI_SZ)
310         {
311             if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
312             if (size)
313             {
314                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
315                 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
316             }
317             if (flags & FLG_ADDREG_APPEND)
318             {
319                 if (!str) return TRUE;
320                 append_multi_sz_value( hkey, value, str, size );
321                 HeapFree( GetProcessHeap(), 0, str );
322                 return TRUE;
323             }
324             /* else fall through to normal string handling */
325         }
326         else
327         {
328             if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
329             if (size)
330             {
331                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
332                 SetupGetStringFieldW( context, 5, str, size, NULL );
333             }
334         }
335
336         if (type == REG_DWORD)
337         {
338             DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
339             TRACE( "setting dword %s to %x\n", debugstr_w(value), dw );
340             RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
341         }
342         else
343         {
344             TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
345             if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
346             else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
347         }
348         HeapFree( GetProcessHeap(), 0, str );
349         return TRUE;
350     }
351     else  /* get the binary data */
352     {
353         BYTE *data = NULL;
354
355         if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
356         if (size)
357         {
358             if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
359             TRACE( "setting binary data %s len %d\n", debugstr_w(value), size );
360             SetupGetBinaryField( context, 5, data, size, NULL );
361         }
362         RegSetValueExW( hkey, value, 0, type, data, size );
363         HeapFree( GetProcessHeap(), 0, data );
364         return TRUE;
365     }
366 }
367
368
369 /***********************************************************************
370  *            registry_callback
371  *
372  * Called once for each AddReg and DelReg entry in a given section.
373  */
374 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
375 {
376     struct registry_callback_info *info = arg;
377     INFCONTEXT context;
378     HKEY root_key, hkey;
379
380     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
381
382     for (; ok; ok = SetupFindNextLine( &context, &context ))
383     {
384         WCHAR buffer[MAX_INF_STRING_LENGTH];
385         INT flags;
386
387         /* get root */
388         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
389             continue;
390         if (!(root_key = get_root_key( buffer, info->default_root )))
391             continue;
392
393         /* get key */
394         if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
395             *buffer = 0;
396
397         /* get flags */
398         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
399
400         if (!info->delete)
401         {
402             if (flags & FLG_ADDREG_DELREG_BIT) continue;  /* ignore this entry */
403         }
404         else
405         {
406             if (!flags) flags = FLG_ADDREG_DELREG_BIT;
407             else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue;  /* ignore this entry */
408         }
409
410         if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
411         {
412             if (RegOpenKeyW( root_key, buffer, &hkey )) continue;  /* ignore if it doesn't exist */
413         }
414         else if (RegCreateKeyW( root_key, buffer, &hkey ))
415         {
416             ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
417             continue;
418         }
419         TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
420
421         /* get value name */
422         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
423             *buffer = 0;
424
425         /* and now do it */
426         if (!do_reg_operation( hkey, buffer, &context, flags ))
427         {
428             RegCloseKey( hkey );
429             return FALSE;
430         }
431         RegCloseKey( hkey );
432     }
433     return TRUE;
434 }
435
436
437 /***********************************************************************
438  *            do_register_dll
439  *
440  * Register or unregister a dll.
441  */
442 static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
443                              INT flags, INT timeout, const WCHAR *args )
444 {
445     HMODULE module;
446     HRESULT res;
447     SP_REGISTER_CONTROL_STATUSW status;
448     IMAGE_NT_HEADERS *nt;
449
450     status.cbSize = sizeof(status);
451     status.FileName = path;
452     status.FailureCode = SPREG_SUCCESS;
453     status.Win32Error = ERROR_SUCCESS;
454
455     if (info->callback)
456     {
457         switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
458                                (UINT_PTR)&status, !info->unregister ))
459         {
460         case FILEOP_ABORT:
461             SetLastError( ERROR_OPERATION_ABORTED );
462             return FALSE;
463         case FILEOP_SKIP:
464             return TRUE;
465         case FILEOP_DOIT:
466             break;
467         }
468     }
469
470     if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
471     {
472         WARN( "could not load %s\n", debugstr_w(path) );
473         status.FailureCode = SPREG_LOADLIBRARY;
474         status.Win32Error = GetLastError();
475         goto done;
476     }
477
478     if ((nt = RtlImageNtHeader( module )) && !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
479     {
480         /* file is an executable, not a dll */
481         STARTUPINFOW startup;
482         PROCESS_INFORMATION info;
483         WCHAR *cmd_line;
484         BOOL res;
485         static const WCHAR format[] = {'"','%','s','"',' ','%','s',0};
486         static const WCHAR default_args[] = {'/','R','e','g','S','e','r','v','e','r',0};
487
488         FreeLibrary( module );
489         module = NULL;
490         if (!args) args = default_args;
491         cmd_line = HeapAlloc( GetProcessHeap(), 0, (strlenW(path) + strlenW(args) + 4) * sizeof(WCHAR) );
492         sprintfW( cmd_line, format, path, args );
493         memset( &startup, 0, sizeof(startup) );
494         startup.cb = sizeof(startup);
495         TRACE( "executing %s\n", debugstr_w(cmd_line) );
496         res = CreateProcessW( NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
497         HeapFree( GetProcessHeap(), 0, cmd_line );
498         if (!res)
499         {
500             status.FailureCode = SPREG_LOADLIBRARY;
501             status.Win32Error = GetLastError();
502             goto done;
503         }
504         CloseHandle( info.hThread );
505
506         if (WaitForSingleObject( info.hProcess, timeout*1000 ) == WAIT_TIMEOUT)
507         {
508             /* timed out, kill the process */
509             TerminateProcess( info.hProcess, 1 );
510             status.FailureCode = SPREG_TIMEOUT;
511             status.Win32Error = ERROR_TIMEOUT;
512         }
513         CloseHandle( info.hProcess );
514         goto done;
515     }
516
517     if (flags & FLG_REGSVR_DLLREGISTER)
518     {
519         const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
520         HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
521
522         if (!func)
523         {
524             status.FailureCode = SPREG_GETPROCADDR;
525             status.Win32Error = GetLastError();
526             goto done;
527         }
528
529         TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
530         res = func();
531
532         if (FAILED(res))
533         {
534             WARN( "calling %s in %s returned error %x\n", entry_point, debugstr_w(path), res );
535             status.FailureCode = SPREG_REGSVR;
536             status.Win32Error = res;
537             goto done;
538         }
539     }
540
541     if (flags & FLG_REGSVR_DLLINSTALL)
542     {
543         HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
544
545         if (!func)
546         {
547             status.FailureCode = SPREG_GETPROCADDR;
548             status.Win32Error = GetLastError();
549             goto done;
550         }
551
552         TRACE( "calling DllInstall(%d,%s) in %s\n",
553                !info->unregister, debugstr_w(args), debugstr_w(path) );
554         res = func( !info->unregister, args );
555
556         if (FAILED(res))
557         {
558             WARN( "calling DllInstall in %s returned error %x\n", debugstr_w(path), res );
559             status.FailureCode = SPREG_REGSVR;
560             status.Win32Error = res;
561             goto done;
562         }
563     }
564
565 done:
566     if (module) FreeLibrary( module );
567     if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
568                                         (UINT_PTR)&status, !info->unregister );
569     return TRUE;
570 }
571
572
573 /***********************************************************************
574  *            register_dlls_callback
575  *
576  * Called once for each RegisterDlls entry in a given section.
577  */
578 static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
579 {
580     struct register_dll_info *info = arg;
581     INFCONTEXT context;
582     BOOL ret = TRUE;
583     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
584
585     for (; ok; ok = SetupFindNextLine( &context, &context ))
586     {
587         WCHAR *path, *args, *p;
588         WCHAR buffer[MAX_INF_STRING_LENGTH];
589         INT flags, timeout;
590
591         /* get directory */
592         if (!(path = PARSER_get_dest_dir( &context ))) continue;
593
594         /* get dll name */
595         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
596             goto done;
597         if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
598                                (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
599         path = p;
600         p += strlenW(p);
601         if (p == path || p[-1] != '\\') *p++ = '\\';
602         strcpyW( p, buffer );
603
604         /* get flags */
605         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
606
607         /* get timeout */
608         if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
609
610         /* get command line */
611         args = NULL;
612         if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
613             args = buffer;
614
615         ret = do_register_dll( info, path, flags, timeout, args );
616
617     done:
618         HeapFree( GetProcessHeap(), 0, path );
619         if (!ret) break;
620     }
621     return ret;
622 }
623
624 /***********************************************************************
625  *            fake_dlls_callback
626  *
627  * Called once for each WineFakeDlls entry in a given section.
628  */
629 static BOOL fake_dlls_callback( HINF hinf, PCWSTR field, void *arg )
630 {
631     INFCONTEXT context;
632     BOOL ret = TRUE;
633     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
634
635     for (; ok; ok = SetupFindNextLine( &context, &context ))
636     {
637         WCHAR *path, *p;
638         WCHAR buffer[MAX_INF_STRING_LENGTH];
639
640         /* get directory */
641         if (!(path = PARSER_get_dest_dir( &context ))) continue;
642
643         /* get dll name */
644         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
645             goto done;
646         if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
647                                (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
648         path = p;
649         p += strlenW(p);
650         if (p == path || p[-1] != '\\') *p++ = '\\';
651         strcpyW( p, buffer );
652
653         /* get source dll */
654         if (SetupGetStringFieldW( &context, 4, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
655             p = buffer;  /* otherwise use target base name as default source */
656
657         create_fake_dll( path, p );  /* ignore errors */
658
659     done:
660         HeapFree( GetProcessHeap(), 0, path );
661         if (!ret) break;
662     }
663     return ret;
664 }
665
666 /***********************************************************************
667  *            update_ini_callback
668  *
669  * Called once for each UpdateInis entry in a given section.
670  */
671 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
672 {
673     INFCONTEXT context;
674
675     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
676
677     for (; ok; ok = SetupFindNextLine( &context, &context ))
678     {
679         WCHAR buffer[MAX_INF_STRING_LENGTH];
680         WCHAR  filename[MAX_INF_STRING_LENGTH];
681         WCHAR  section[MAX_INF_STRING_LENGTH];
682         WCHAR  entry[MAX_INF_STRING_LENGTH];
683         WCHAR  string[MAX_INF_STRING_LENGTH];
684         LPWSTR divider;
685
686         if (!SetupGetStringFieldW( &context, 1, filename,
687                                    sizeof(filename)/sizeof(WCHAR), NULL ))
688             continue;
689
690         if (!SetupGetStringFieldW( &context, 2, section,
691                                    sizeof(section)/sizeof(WCHAR), NULL ))
692             continue;
693
694         if (!SetupGetStringFieldW( &context, 4, buffer,
695                                    sizeof(buffer)/sizeof(WCHAR), NULL ))
696             continue;
697
698         divider = strchrW(buffer,'=');
699         if (divider)
700         {
701             *divider = 0;
702             strcpyW(entry,buffer);
703             divider++;
704             strcpyW(string,divider);
705         }
706         else
707         {
708             strcpyW(entry,buffer);
709             string[0]=0;
710         }
711
712         TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
713                debugstr_w(string),debugstr_w(section),debugstr_w(filename));
714         WritePrivateProfileStringW(section,entry,string,filename);
715
716     }
717     return TRUE;
718 }
719
720 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
721 {
722     FIXME( "should update ini fields %s\n", debugstr_w(field) );
723     return TRUE;
724 }
725
726 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
727 {
728     FIXME( "should do ini2reg %s\n", debugstr_w(field) );
729     return TRUE;
730 }
731
732 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
733 {
734     FIXME( "should do logconf %s\n", debugstr_w(field) );
735     return TRUE;
736 }
737
738 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
739 {
740     FIXME( "should do bitreg %s\n", debugstr_w(field) );
741     return TRUE;
742 }
743
744 static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg )
745 {
746     FIXME( "should do profile items %s\n", debugstr_w(field) );
747     return TRUE;
748 }
749
750 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
751 {
752     FIXME( "should do copy inf %s\n", debugstr_w(field) );
753     return TRUE;
754 }
755
756
757 /***********************************************************************
758  *            iterate_section_fields
759  *
760  * Iterate over all fields of a certain key of a certain section
761  */
762 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
763                                     iterate_fields_func callback, void *arg )
764 {
765     WCHAR static_buffer[200];
766     WCHAR *buffer = static_buffer;
767     DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
768     INFCONTEXT context;
769     BOOL ret = FALSE;
770
771     BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
772     while (ok)
773     {
774         UINT i, count = SetupGetFieldCount( &context );
775         for (i = 1; i <= count; i++)
776         {
777             if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
778                 goto done;
779             if (!callback( hinf, buffer, arg ))
780             {
781                 WARN("callback failed for %s %s err %d\n",
782                      debugstr_w(section), debugstr_w(buffer), GetLastError() );
783                 goto done;
784             }
785         }
786         ok = SetupFindNextMatchLineW( &context, key, &context );
787     }
788     ret = TRUE;
789  done:
790     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
791     return ret;
792 }
793
794
795 /***********************************************************************
796  *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
797  */
798 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
799                                               PCSTR section, PCSTR src_root, UINT flags )
800 {
801     UNICODE_STRING sectionW;
802     BOOL ret = FALSE;
803
804     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
805     {
806         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
807         return FALSE;
808     }
809     if (!src_root)
810         ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
811                                                 NULL, flags );
812     else
813     {
814         UNICODE_STRING srcW;
815         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
816         {
817             ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
818                                                     srcW.Buffer, flags );
819             RtlFreeUnicodeString( &srcW );
820         }
821         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
822     }
823     RtlFreeUnicodeString( &sectionW );
824     return ret;
825 }
826
827
828 /***********************************************************************
829  *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
830  */
831 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
832                                               PCWSTR section, PCWSTR src_root, UINT flags )
833 {
834     struct files_callback_info info;
835
836     info.queue      = queue;
837     info.src_root   = src_root;
838     info.copy_flags = flags;
839     info.layout     = hlayout;
840     return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
841 }
842
843
844 /***********************************************************************
845  *            SetupInstallFromInfSectionA   (SETUPAPI.@)
846  */
847 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
848                                          HKEY key_root, PCSTR src_root, UINT copy_flags,
849                                          PSP_FILE_CALLBACK_A callback, PVOID context,
850                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
851 {
852     UNICODE_STRING sectionW, src_rootW;
853     struct callback_WtoA_context ctx;
854     BOOL ret = FALSE;
855
856     src_rootW.Buffer = NULL;
857     if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
858     {
859         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
860         return FALSE;
861     }
862
863     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
864     {
865         ctx.orig_context = context;
866         ctx.orig_handler = callback;
867         ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
868                                            src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
869                                            &ctx, devinfo, devinfo_data );
870         RtlFreeUnicodeString( &sectionW );
871     }
872     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
873
874     RtlFreeUnicodeString( &src_rootW );
875     return ret;
876 }
877
878
879 /***********************************************************************
880  *            SetupInstallFromInfSectionW   (SETUPAPI.@)
881  */
882 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
883                                          HKEY key_root, PCWSTR src_root, UINT copy_flags,
884                                          PSP_FILE_CALLBACK_W callback, PVOID context,
885                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
886 {
887     if (flags & SPINST_FILES)
888     {
889         struct files_callback_info info;
890         HSPFILEQ queue;
891         BOOL ret;
892
893         if (!(queue = SetupOpenFileQueue())) return FALSE;
894         info.queue      = queue;
895         info.src_root   = src_root;
896         info.copy_flags = copy_flags;
897         info.layout     = hinf;
898         ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
899                iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
900                iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) &&
901                SetupCommitFileQueueW( owner, queue, callback, context ));
902         SetupCloseFileQueue( queue );
903         if (!ret) return FALSE;
904     }
905     if (flags & SPINST_INIFILES)
906     {
907         if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
908             !iterate_section_fields( hinf, section, UpdateIniFields,
909                                      update_ini_fields_callback, NULL ))
910             return FALSE;
911     }
912     if (flags & SPINST_INI2REG)
913     {
914         if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
915             return FALSE;
916     }
917     if (flags & SPINST_LOGCONFIG)
918     {
919         if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
920             return FALSE;
921     }
922     if (flags & SPINST_REGSVR)
923     {
924         struct register_dll_info info;
925
926         info.unregister = FALSE;
927         if (flags & SPINST_REGISTERCALLBACKAWARE)
928         {
929             info.callback         = callback;
930             info.callback_context = context;
931         }
932         else info.callback = NULL;
933
934         if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
935             return FALSE;
936
937         if (!iterate_section_fields( hinf, section, WineFakeDlls, fake_dlls_callback, NULL ))
938             return FALSE;
939     }
940     if (flags & SPINST_UNREGSVR)
941     {
942         struct register_dll_info info;
943
944         info.unregister = TRUE;
945         if (flags & SPINST_REGISTERCALLBACKAWARE)
946         {
947             info.callback         = callback;
948             info.callback_context = context;
949         }
950         else info.callback = NULL;
951
952         if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
953             return FALSE;
954     }
955     if (flags & SPINST_REGISTRY)
956     {
957         struct registry_callback_info info;
958
959         info.default_root = key_root;
960         info.delete = TRUE;
961         if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
962             return FALSE;
963         info.delete = FALSE;
964         if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
965             return FALSE;
966     }
967     if (flags & SPINST_BITREG)
968     {
969         if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
970             return FALSE;
971     }
972     if (flags & SPINST_PROFILEITEMS)
973     {
974         if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
975             return FALSE;
976     }
977     if (flags & SPINST_COPYINF)
978     {
979         if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
980             return FALSE;
981     }
982
983     return TRUE;
984 }
985
986
987 /***********************************************************************
988  *              InstallHinfSectionW  (SETUPAPI.@)
989  *
990  * NOTE: 'cmdline' is <section> <mode> <path> from
991  *   RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
992  */
993 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
994 {
995 #ifdef __i386__
996     static const WCHAR nt_platformW[] = {'.','n','t','x','8','6',0};
997 #elif defined(__x86_64)
998     static const WCHAR nt_platformW[] = {'.','n','t','a','m','d','6','4',0};
999 #else  /* FIXME: other platforms */
1000     static const WCHAR nt_platformW[] = {'.','n','t',0};
1001 #endif
1002     static const WCHAR nt_genericW[] = {'.','n','t',0};
1003
1004     WCHAR *s, *d, *path, section[MAX_PATH + sizeof(nt_platformW)/sizeof(WCHAR)];
1005     void *callback_context;
1006     UINT mode, in_quotes, bcount;
1007     HINF hinf;
1008
1009     TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
1010
1011     lstrcpynW( section, cmdline, MAX_PATH );
1012
1013     if (!(s = strchrW( section, ' ' ))) return;
1014     *s++ = 0;
1015     while (*s == ' ') s++;
1016     mode = atoiW( s );
1017
1018     if (!(s = strchrW( s, ' ' ))) return;
1019     while (*s == ' ') s++;
1020
1021     /* The inf path may be quoted. Code adapted from CommandLineToArgvW() */
1022     bcount=0;
1023     in_quotes=0;
1024     path=d=s;
1025     while (*s)
1026     {
1027         if (*s==0 || ((*s=='\t' || *s==' ') && !in_quotes)) {
1028             /* end of this command line argument */
1029             break;
1030         } else if (*s=='\\') {
1031             /* '\\' */
1032             *d++=*s++;
1033             bcount++;
1034         } else if (*s=='"') {
1035             /* '"' */
1036             if ((bcount & 1)==0) {
1037                 /* Preceded by an even number of '\', this is half that
1038                  * number of '\', plus a quote which we erase.
1039                  */
1040                 d-=bcount/2;
1041                 in_quotes=!in_quotes;
1042                 s++;
1043             } else {
1044                 /* Preceded by an odd number of '\', this is half that
1045                  * number of '\' followed by a '"'
1046                  */
1047                 d=d-bcount/2-1;
1048                 *d++='"';
1049                 s++;
1050             }
1051             bcount=0;
1052         } else {
1053             /* a regular character */
1054             *d++=*s++;
1055             bcount=0;
1056         }
1057     }
1058     *d=0;
1059
1060     hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
1061     if (hinf == INVALID_HANDLE_VALUE) return;
1062
1063     if (!(GetVersion() & 0x80000000))
1064     {
1065         INFCONTEXT context;
1066
1067         /* check for <section>.ntx86 (or corresponding name for the current platform)
1068          * and then <section>.nt */
1069         s = section + strlenW(section);
1070         memcpy( s, nt_platformW, sizeof(nt_platformW) );
1071         if (!(SetupFindFirstLineW( hinf, section, NULL, &context )))
1072         {
1073             memcpy( s, nt_genericW, sizeof(nt_genericW) );
1074             if (!(SetupFindFirstLineW( hinf, section, NULL, &context ))) *s = 0;
1075         }
1076         if (*s) TRACE( "using section %s instead\n", debugstr_w(section) );
1077     }
1078
1079     callback_context = SetupInitDefaultQueueCallback( hwnd );
1080     SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER,
1081                                  SetupDefaultQueueCallbackW, callback_context,
1082                                  NULL, NULL );
1083     SetupTermDefaultQueueCallback( callback_context );
1084     SetupCloseInfFile( hinf );
1085
1086     /* FIXME: should check the mode and maybe reboot */
1087     /* there isn't much point in doing that since we */
1088     /* don't yet handle deferred file copies anyway. */
1089 }
1090
1091
1092 /***********************************************************************
1093  *              InstallHinfSectionA  (SETUPAPI.@)
1094  */
1095 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
1096 {
1097     UNICODE_STRING cmdlineW;
1098
1099     if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1100     {
1101         InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1102         RtlFreeUnicodeString( &cmdlineW );
1103     }
1104 }