Display a drive icon for the unix root directory.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
80
81 /***********************************************************************
82  *            get_field_string
83  *
84  * Retrieve the contents of a field, dynamically growing the buffer if necessary.
85  */
86 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
87                                 WCHAR *static_buffer, DWORD *size )
88 {
89     DWORD required;
90
91     if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
92     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
93     {
94         /* now grow the buffer */
95         if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
96         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
97         *size = required;
98         if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
99     }
100     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
101     return NULL;
102 }
103
104
105 /***********************************************************************
106  *            copy_files_callback
107  *
108  * Called once for each CopyFiles entry in a given section.
109  */
110 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
111 {
112     struct files_callback_info *info = arg;
113
114     if (field[0] == '@')  /* special case: copy single file */
115         SetupQueueDefaultCopyW( info->queue, info->layout, info->src_root, NULL, field, info->copy_flags );
116     else
117         SetupQueueCopySectionW( info->queue, info->src_root, info->layout, hinf, field, info->copy_flags );
118     return TRUE;
119 }
120
121
122 /***********************************************************************
123  *            delete_files_callback
124  *
125  * Called once for each DelFiles entry in a given section.
126  */
127 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
128 {
129     struct files_callback_info *info = arg;
130     SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
131     return TRUE;
132 }
133
134
135 /***********************************************************************
136  *            rename_files_callback
137  *
138  * Called once for each RenFiles entry in a given section.
139  */
140 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
141 {
142     struct files_callback_info *info = arg;
143     SetupQueueRenameSectionW( info->queue, hinf, 0, field );
144     return TRUE;
145 }
146
147
148 /***********************************************************************
149  *            get_root_key
150  *
151  * Retrieve the registry root key from its name.
152  */
153 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
154 {
155     static const WCHAR HKCR[] = {'H','K','C','R',0};
156     static const WCHAR HKCU[] = {'H','K','C','U',0};
157     static const WCHAR HKLM[] = {'H','K','L','M',0};
158     static const WCHAR HKU[]  = {'H','K','U',0};
159     static const WCHAR HKR[]  = {'H','K','R',0};
160
161     if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
162     if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
163     if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
164     if (!strcmpiW( name, HKU )) return HKEY_USERS;
165     if (!strcmpiW( name, HKR )) return def_root;
166     return 0;
167 }
168
169
170 /***********************************************************************
171  *            append_multi_sz_value
172  *
173  * Append a multisz string to a multisz registry value.
174  */
175 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
176                                    DWORD str_size )
177 {
178     DWORD size, type, total;
179     WCHAR *buffer, *p;
180
181     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
182     if (type != REG_MULTI_SZ) return;
183
184     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
185     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
186
187     /* compare each string against all the existing ones */
188     total = size;
189     while (*strings)
190     {
191         int len = strlenW(strings) + 1;
192
193         for (p = buffer; *p; p += strlenW(p) + 1)
194             if (!strcmpiW( p, strings )) break;
195
196         if (!*p)  /* not found, need to append it */
197         {
198             memcpy( p, strings, len * sizeof(WCHAR) );
199             p[len] = 0;
200             total += len;
201         }
202         strings += len;
203     }
204     if (total != size)
205     {
206         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
207         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
208     }
209  done:
210     HeapFree( GetProcessHeap(), 0, buffer );
211 }
212
213
214 /***********************************************************************
215  *            delete_multi_sz_value
216  *
217  * Remove a string from a multisz registry value.
218  */
219 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
220 {
221     DWORD size, type;
222     WCHAR *buffer, *src, *dst;
223
224     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
225     if (type != REG_MULTI_SZ) return;
226     /* allocate double the size, one for value before and one for after */
227     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
228     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
229     src = buffer;
230     dst = buffer + size;
231     while (*src)
232     {
233         int len = strlenW(src) + 1;
234         if (strcmpiW( src, string ))
235         {
236             memcpy( dst, src, len * sizeof(WCHAR) );
237             dst += len;
238         }
239         src += len;
240     }
241     *dst++ = 0;
242     if (dst != buffer + 2*size)  /* did we remove something? */
243     {
244         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
245         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
246                         (BYTE *)(buffer + size), dst - (buffer + size) );
247     }
248  done:
249     HeapFree( GetProcessHeap(), 0, buffer );
250 }
251
252
253 /***********************************************************************
254  *            do_reg_operation
255  *
256  * Perform an add/delete registry operation depending on the flags.
257  */
258 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
259 {
260     DWORD type, size;
261
262     if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL))  /* deletion */
263     {
264         if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
265         {
266             if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
267             {
268                 WCHAR *str;
269
270                 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
271                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
272                 SetupGetStringFieldW( context, 5, str, size, NULL );
273                 delete_multi_sz_value( hkey, value, str );
274                 HeapFree( GetProcessHeap(), 0, str );
275             }
276             else RegDeleteValueW( hkey, value );
277         }
278         else RegDeleteKeyW( hkey, NULL );
279         return TRUE;
280     }
281
282     if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
283
284     if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
285     {
286         BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
287         if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
288         if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
289     }
290
291     switch(flags & FLG_ADDREG_TYPE_MASK)
292     {
293     case FLG_ADDREG_TYPE_SZ:        type = REG_SZ; break;
294     case FLG_ADDREG_TYPE_MULTI_SZ:  type = REG_MULTI_SZ; break;
295     case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
296     case FLG_ADDREG_TYPE_BINARY:    type = REG_BINARY; break;
297     case FLG_ADDREG_TYPE_DWORD:     type = REG_DWORD; break;
298     case FLG_ADDREG_TYPE_NONE:      type = REG_NONE; break;
299     default:                        type = flags >> 16; break;
300     }
301
302     if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
303         (type == REG_DWORD && SetupGetFieldCount(context) == 5))
304     {
305         static const WCHAR empty;
306         WCHAR *str = NULL;
307
308         if (type == REG_MULTI_SZ)
309         {
310             if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
311             if (size)
312             {
313                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
314                 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
315             }
316             if (flags & FLG_ADDREG_APPEND)
317             {
318                 if (!str) return TRUE;
319                 append_multi_sz_value( hkey, value, str, size );
320                 HeapFree( GetProcessHeap(), 0, str );
321                 return TRUE;
322             }
323             /* else fall through to normal string handling */
324         }
325         else
326         {
327             if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
328             if (size)
329             {
330                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
331                 SetupGetStringFieldW( context, 5, str, size, NULL );
332             }
333         }
334
335         if (type == REG_DWORD)
336         {
337             DWORD dw = str ? strtoulW( str, NULL, 16 ) : 0;
338             TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw );
339             RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
340         }
341         else
342         {
343             TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
344             if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
345             else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
346         }
347         HeapFree( GetProcessHeap(), 0, str );
348         return TRUE;
349     }
350     else  /* get the binary data */
351     {
352         BYTE *data = NULL;
353
354         if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
355         if (size)
356         {
357             if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
358             TRACE( "setting binary data %s len %ld\n", debugstr_w(value), size );
359             SetupGetBinaryField( context, 5, data, size, NULL );
360         }
361         RegSetValueExW( hkey, value, 0, type, data, size );
362         HeapFree( GetProcessHeap(), 0, data );
363         return TRUE;
364     }
365 }
366
367
368 /***********************************************************************
369  *            registry_callback
370  *
371  * Called once for each AddReg and DelReg entry in a given section.
372  */
373 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
374 {
375     struct registry_callback_info *info = arg;
376     INFCONTEXT context;
377     HKEY root_key, hkey;
378
379     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
380
381     for (; ok; ok = SetupFindNextLine( &context, &context ))
382     {
383         WCHAR buffer[MAX_INF_STRING_LENGTH];
384         INT flags;
385
386         /* get root */
387         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
388             continue;
389         if (!(root_key = get_root_key( buffer, info->default_root )))
390             continue;
391
392         /* get key */
393         if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
394             *buffer = 0;
395
396         /* get flags */
397         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
398
399         if (!info->delete)
400         {
401             if (flags & FLG_ADDREG_DELREG_BIT) continue;  /* ignore this entry */
402         }
403         else
404         {
405             if (!flags) flags = FLG_ADDREG_DELREG_BIT;
406             else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue;  /* ignore this entry */
407         }
408
409         if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
410         {
411             if (RegOpenKeyW( root_key, buffer, &hkey )) continue;  /* ignore if it doesn't exist */
412         }
413         else if (RegCreateKeyW( root_key, buffer, &hkey ))
414         {
415             ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
416             continue;
417         }
418         TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
419
420         /* get value name */
421         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
422             *buffer = 0;
423
424         /* and now do it */
425         if (!do_reg_operation( hkey, buffer, &context, flags ))
426         {
427             RegCloseKey( hkey );
428             return FALSE;
429         }
430         RegCloseKey( hkey );
431     }
432     return TRUE;
433 }
434
435
436 /***********************************************************************
437  *            do_register_dll
438  *
439  * Register or unregister a dll.
440  */
441 static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
442                              INT flags, INT timeout, const WCHAR *args )
443 {
444     HMODULE module;
445     HRESULT res;
446     SP_REGISTER_CONTROL_STATUSW status;
447
448     status.cbSize = sizeof(status);
449     status.FileName = path;
450     status.FailureCode = SPREG_SUCCESS;
451     status.Win32Error = ERROR_SUCCESS;
452
453     if (info->callback)
454     {
455         switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
456                                (UINT_PTR)&status, !info->unregister ))
457         {
458         case FILEOP_ABORT:
459             SetLastError( ERROR_OPERATION_ABORTED );
460             return FALSE;
461         case FILEOP_SKIP:
462             return TRUE;
463         case FILEOP_DOIT:
464             break;
465         }
466     }
467
468     if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
469     {
470         WARN( "could not load %s\n", debugstr_w(path) );
471         status.FailureCode = SPREG_LOADLIBRARY;
472         status.Win32Error = GetLastError();
473         goto done;
474     }
475
476     if (flags & FLG_REGSVR_DLLREGISTER)
477     {
478         const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
479         HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
480
481         if (!func)
482         {
483             status.FailureCode = SPREG_GETPROCADDR;
484             status.Win32Error = GetLastError();
485             goto done;
486         }
487
488         TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
489         res = func();
490
491         if (FAILED(res))
492         {
493             WARN( "calling %s in %s returned error %lx\n", entry_point, debugstr_w(path), res );
494             status.FailureCode = SPREG_REGSVR;
495             status.Win32Error = res;
496             goto done;
497         }
498     }
499
500     if (flags & FLG_REGSVR_DLLINSTALL)
501     {
502         HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
503
504         if (!func)
505         {
506             status.FailureCode = SPREG_GETPROCADDR;
507             status.Win32Error = GetLastError();
508             goto done;
509         }
510
511         TRACE( "calling DllInstall(%d,%s) in %s\n",
512                !info->unregister, debugstr_w(args), debugstr_w(path) );
513         res = func( !info->unregister, args );
514
515         if (FAILED(res))
516         {
517             WARN( "calling DllInstall in %s returned error %lx\n", debugstr_w(path), res );
518             status.FailureCode = SPREG_REGSVR;
519             status.Win32Error = res;
520             goto done;
521         }
522     }
523
524 done:
525     if (module) FreeLibrary( module );
526     if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
527                                         (UINT_PTR)&status, !info->unregister );
528     return TRUE;
529 }
530
531
532 /***********************************************************************
533  *            register_dlls_callback
534  *
535  * Called once for each RegisterDlls entry in a given section.
536  */
537 static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
538 {
539     struct register_dll_info *info = arg;
540     INFCONTEXT context;
541     BOOL ret = TRUE;
542     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
543
544     for (; ok; ok = SetupFindNextLine( &context, &context ))
545     {
546         WCHAR *path, *args, *p;
547         WCHAR buffer[MAX_INF_STRING_LENGTH];
548         INT flags, timeout;
549
550         /* get directory */
551         if (!(path = PARSER_get_dest_dir( &context ))) continue;
552
553         /* get dll name */
554         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
555             goto done;
556         if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
557                                (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
558         path = p;
559         p += strlenW(p);
560         if (p == path || p[-1] != '\\') *p++ = '\\';
561         strcpyW( p, buffer );
562
563         /* get flags */
564         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
565
566         /* get timeout */
567         if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
568
569         /* get command line */
570         args = NULL;
571         if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
572             args = buffer;
573
574         ret = do_register_dll( info, path, flags, timeout, args );
575
576     done:
577         HeapFree( GetProcessHeap(), 0, path );
578         if (!ret) break;
579     }
580     return ret;
581 }
582
583 /***********************************************************************
584  *            update_ini_callback
585  *
586  * Called once for each UpdateInis entry in a given section.
587  */
588 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
589 {
590     INFCONTEXT context;
591
592     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
593
594     for (; ok; ok = SetupFindNextLine( &context, &context ))
595     {
596         WCHAR buffer[MAX_INF_STRING_LENGTH];
597         WCHAR  filename[MAX_INF_STRING_LENGTH];
598         WCHAR  section[MAX_INF_STRING_LENGTH];
599         WCHAR  entry[MAX_INF_STRING_LENGTH];
600         WCHAR  string[MAX_INF_STRING_LENGTH];
601         LPWSTR divider;
602
603         if (!SetupGetStringFieldW( &context, 1, filename,
604                                    sizeof(filename)/sizeof(WCHAR), NULL ))
605             continue;
606
607         if (!SetupGetStringFieldW( &context, 2, section,
608                                    sizeof(section)/sizeof(WCHAR), NULL ))
609             continue;
610
611         if (!SetupGetStringFieldW( &context, 4, buffer,
612                                    sizeof(buffer)/sizeof(WCHAR), NULL ))
613             continue;
614
615         divider = strchrW(buffer,'=');
616         if (divider)
617         {
618             *divider = 0;
619             strcpyW(entry,buffer);
620             divider++;
621             strcpyW(string,divider);
622         }
623         else
624         {
625             strcpyW(entry,buffer);
626             string[0]=0;
627         }
628
629         TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
630                debugstr_w(string),debugstr_w(section),debugstr_w(filename));
631         WritePrivateProfileStringW(section,entry,string,filename);
632
633     }
634     return TRUE;
635 }
636
637 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
638 {
639     FIXME( "should update ini fields %s\n", debugstr_w(field) );
640     return TRUE;
641 }
642
643 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
644 {
645     FIXME( "should do ini2reg %s\n", debugstr_w(field) );
646     return TRUE;
647 }
648
649 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
650 {
651     FIXME( "should do logconf %s\n", debugstr_w(field) );
652     return TRUE;
653 }
654
655 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
656 {
657     FIXME( "should do bitreg %s\n", debugstr_w(field) );
658     return TRUE;
659 }
660
661 static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg )
662 {
663     FIXME( "should do profile items %s\n", debugstr_w(field) );
664     return TRUE;
665 }
666
667 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
668 {
669     FIXME( "should do copy inf %s\n", debugstr_w(field) );
670     return TRUE;
671 }
672
673
674 /***********************************************************************
675  *            iterate_section_fields
676  *
677  * Iterate over all fields of a certain key of a certain section
678  */
679 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
680                                     iterate_fields_func callback, void *arg )
681 {
682     WCHAR static_buffer[200];
683     WCHAR *buffer = static_buffer;
684     DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
685     INFCONTEXT context;
686     BOOL ret = FALSE;
687
688     BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
689     while (ok)
690     {
691         UINT i, count = SetupGetFieldCount( &context );
692         for (i = 1; i <= count; i++)
693         {
694             if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
695                 goto done;
696             if (!callback( hinf, buffer, arg ))
697             {
698                 WARN("callback failed for %s %s err %ld\n",
699                      debugstr_w(section), debugstr_w(buffer), GetLastError() );
700                 goto done;
701             }
702         }
703         ok = SetupFindNextMatchLineW( &context, key, &context );
704     }
705     ret = TRUE;
706  done:
707     if (buffer && buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
708     return ret;
709 }
710
711
712 /***********************************************************************
713  *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
714  */
715 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
716                                               PCSTR section, PCSTR src_root, UINT flags )
717 {
718     UNICODE_STRING sectionW;
719     BOOL ret = FALSE;
720
721     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
722     {
723         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
724         return FALSE;
725     }
726     if (!src_root)
727         ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
728                                                 NULL, flags );
729     else
730     {
731         UNICODE_STRING srcW;
732         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
733         {
734             ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
735                                                     srcW.Buffer, flags );
736             RtlFreeUnicodeString( &srcW );
737         }
738         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
739     }
740     RtlFreeUnicodeString( &sectionW );
741     return ret;
742 }
743
744
745 /***********************************************************************
746  *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
747  */
748 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
749                                               PCWSTR section, PCWSTR src_root, UINT flags )
750 {
751     struct files_callback_info info;
752
753     info.queue      = queue;
754     info.src_root   = src_root;
755     info.copy_flags = flags;
756     info.layout     = hlayout;
757     return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
758 }
759
760
761 /***********************************************************************
762  *            SetupInstallFromInfSectionA   (SETUPAPI.@)
763  */
764 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
765                                          HKEY key_root, PCSTR src_root, UINT copy_flags,
766                                          PSP_FILE_CALLBACK_A callback, PVOID context,
767                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
768 {
769     UNICODE_STRING sectionW, src_rootW;
770     struct callback_WtoA_context ctx;
771     BOOL ret = FALSE;
772
773     src_rootW.Buffer = NULL;
774     if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
775     {
776         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
777         return FALSE;
778     }
779
780     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
781     {
782         ctx.orig_context = context;
783         ctx.orig_handler = callback;
784         ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
785                                            src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
786                                            &ctx, devinfo, devinfo_data );
787         RtlFreeUnicodeString( &sectionW );
788     }
789     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
790
791     RtlFreeUnicodeString( &src_rootW );
792     return ret;
793 }
794
795
796 /***********************************************************************
797  *            SetupInstallFromInfSectionW   (SETUPAPI.@)
798  */
799 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
800                                          HKEY key_root, PCWSTR src_root, UINT copy_flags,
801                                          PSP_FILE_CALLBACK_W callback, PVOID context,
802                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
803 {
804     if (flags & SPINST_FILES)
805     {
806         struct files_callback_info info;
807         HSPFILEQ queue;
808         BOOL ret;
809
810         if (!(queue = SetupOpenFileQueue())) return FALSE;
811         info.queue      = queue;
812         info.src_root   = src_root;
813         info.copy_flags = copy_flags;
814         info.layout     = hinf;
815         ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
816                iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
817                iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) &&
818                SetupCommitFileQueueW( owner, queue, callback, context ));
819         SetupCloseFileQueue( queue );
820         if (!ret) return FALSE;
821     }
822     if (flags & SPINST_INIFILES)
823     {
824         if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
825             !iterate_section_fields( hinf, section, UpdateIniFields,
826                                      update_ini_fields_callback, NULL ))
827             return FALSE;
828     }
829     if (flags & SPINST_INI2REG)
830     {
831         if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
832             return FALSE;
833     }
834     if (flags & SPINST_LOGCONFIG)
835     {
836         if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
837             return FALSE;
838     }
839     if (flags & SPINST_REGSVR)
840     {
841         struct register_dll_info info;
842
843         info.unregister = FALSE;
844         if (flags & SPINST_REGISTERCALLBACKAWARE)
845         {
846             info.callback         = callback;
847             info.callback_context = context;
848         }
849         else info.callback = NULL;
850
851         if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
852             return FALSE;
853     }
854     if (flags & SPINST_UNREGSVR)
855     {
856         struct register_dll_info info;
857
858         info.unregister = TRUE;
859         if (flags & SPINST_REGISTERCALLBACKAWARE)
860         {
861             info.callback         = callback;
862             info.callback_context = context;
863         }
864         else info.callback = NULL;
865
866         if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
867             return FALSE;
868     }
869     if (flags & SPINST_REGISTRY)
870     {
871         struct registry_callback_info info;
872
873         info.default_root = key_root;
874         info.delete = TRUE;
875         if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
876             return FALSE;
877         info.delete = FALSE;
878         if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
879             return FALSE;
880     }
881     if (flags & SPINST_BITREG)
882     {
883         if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
884             return FALSE;
885     }
886     if (flags & SPINST_PROFILEITEMS)
887     {
888         if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
889             return FALSE;
890     }
891     if (flags & SPINST_COPYINF)
892     {
893         if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
894             return FALSE;
895     }
896
897     return TRUE;
898 }
899
900
901 /***********************************************************************
902  *              InstallHinfSectionW  (SETUPAPI.@)
903  *
904  * NOTE: 'cmdline' is <section> <mode> <path> from
905  *   RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
906  */
907 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
908 {
909     WCHAR *p, *path, section[MAX_PATH];
910     void *callback_context;
911     UINT mode;
912     HINF hinf;
913
914     TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
915
916     lstrcpynW( section, cmdline, sizeof(section)/sizeof(WCHAR) );
917
918     if (!(p = strchrW( section, ' ' ))) return;
919     *p++ = 0;
920     while (*p == ' ') p++;
921     mode = atoiW( p );
922
923     if (!(p = strchrW( p, ' ' ))) return;
924     path = p + 1;
925     while (*path == ' ') path++;
926
927     hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
928     if (hinf == INVALID_HANDLE_VALUE) return;
929
930     callback_context = SetupInitDefaultQueueCallback( hwnd );
931     SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER,
932                                  SetupDefaultQueueCallbackW, callback_context,
933                                  NULL, NULL );
934     SetupTermDefaultQueueCallback( callback_context );
935     SetupCloseInfFile( hinf );
936
937     /* FIXME: should check the mode and maybe reboot */
938     /* there isn't much point in doing that since we */
939     /* don't yet handle deferred file copies anyway. */
940 }
941
942
943 /***********************************************************************
944  *              InstallHinfSectionA  (SETUPAPI.@)
945  */
946 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
947 {
948     UNICODE_STRING cmdlineW;
949
950     if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
951     {
952         InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
953         RtlFreeUnicodeString( &cmdlineW );
954     }
955 }