xref: /reactos/dll/win32/setupapi/install.c (revision a8b33400)
1 /*
2  * Setupapi install routines
3  *
4  * Copyright 2002 Alexandre Julliard for CodeWeavers
5  *           2005-2006 Herv� Poussineau (hpoussin@reactos.org)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "setupapi_private.h"
23 
24 #include <winsvc.h>
25 #include <ndk/cmfuncs.h>
26 
27 /* Unicode constants */
28 static const WCHAR BackSlash[] = {'\\',0};
29 static const WCHAR GroupOrderListKey[] = {'S','Y','S','T','E','M','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','C','o','n','t','r','o','l','\\','G','r','o','u','p','O','r','d','e','r','L','i','s','t',0};
30 static const WCHAR InfDirectory[] = {'i','n','f','\\',0};
31 static const WCHAR OemFileMask[] = {'o','e','m','*','.','i','n','f',0};
32 static const WCHAR OemFileSpecification[] = {'o','e','m','%','l','u','.','i','n','f',0};
33 static const WCHAR DotLnk[] = {'.','l','n','k',0};
34 static const WCHAR DotServices[]  = {'.','S','e','r','v','i','c','e','s',0};
35 
36 static const WCHAR DependenciesKey[] = {'D','e','p','e','n','d','e','n','c','i','e','s',0};
37 static const WCHAR DescriptionKey[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
38 static const WCHAR DisplayNameKey[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
39 static const WCHAR ErrorControlKey[] = {'E','r','r','o','r','C','o','n','t','r','o','l',0};
40 static const WCHAR LoadOrderGroupKey[] = {'L','o','a','d','O','r','d','e','r','G','r','o','u','p',0};
41 static const WCHAR SecurityKey[] = {'S','e','c','u','r','i','t','y',0};
42 static const WCHAR ServiceBinaryKey[] = {'S','e','r','v','i','c','e','B','i','n','a','r','y',0};
43 static const WCHAR ServiceTypeKey[] = {'S','e','r','v','i','c','e','T','y','p','e',0};
44 static const WCHAR StartTypeKey[] = {'S','t','a','r','t','T','y','p','e',0};
45 static const WCHAR StartNameKey[] = {'S','t','a','r','t','N','a','m','e',0};
46 
47 static const WCHAR Name[] = {'N','a','m','e',0};
48 static const WCHAR CmdLine[] = {'C','m','d','L','i','n','e',0};
49 static const WCHAR SubDir[] = {'S','u','b','D','i','r',0};
50 static const WCHAR WorkingDir[] = {'W','o','r','k','i','n','g','D','i','r',0};
51 static const WCHAR IconPath[] = {'I','c','o','n','P','a','t','h',0};
52 static const WCHAR IconIndex[] = {'I','c','o','n','I','n','d','e','x',0};
53 static const WCHAR HotKey[] = {'H','o','t','K','e','y',0};
54 static const WCHAR InfoTip[] = {'I','n','f','o','T','i','p',0};
55 static const WCHAR DisplayResource[] = {'D','i','s','p','l','a','y','R','e','s','o','u','r','c','e',0};
56 
57 /* info passed to callback functions dealing with files */
58 struct files_callback_info
59 {
60     HSPFILEQ queue;
61     PCWSTR   src_root;
62     UINT     copy_flags;
63     HINF     layout;
64 };
65 
66 /* info passed to callback functions dealing with the registry */
67 struct registry_callback_info
68 {
69     HKEY default_root;
70     BOOL delete;
71 };
72 
73 /* info passed to callback functions dealing with registering dlls */
74 struct register_dll_info
75 {
76     PSP_FILE_CALLBACK_W callback;
77     PVOID               callback_context;
78     BOOL                unregister;
79 };
80 
81 /* info passed to callback functions dealing with Needs directives */
82 struct needs_callback_info
83 {
84     UINT type;
85 
86     HWND             owner;
87     UINT             flags;
88     HKEY             key_root;
89     LPCWSTR          src_root;
90     UINT             copy_flags;
91     PVOID            callback;
92     PVOID            context;
93     HDEVINFO         devinfo;
94     PSP_DEVINFO_DATA devinfo_data;
95     PVOID            reserved1;
96     PVOID            reserved2;
97 };
98 
99 typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
100 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value);
101 typedef HRESULT (WINAPI *COINITIALIZE)(IN LPVOID pvReserved);
102 typedef HRESULT (WINAPI *COCREATEINSTANCE)(IN REFCLSID rclsid, IN LPUNKNOWN pUnkOuter, IN DWORD dwClsContext, IN REFIID riid, OUT LPVOID *ppv);
103 typedef HRESULT (WINAPI *COUNINITIALIZE)(VOID);
104 
105 /* Unicode constants */
106 static const WCHAR AddService[] = {'A','d','d','S','e','r','v','i','c','e',0};
107 static const WCHAR CopyFiles[]  = {'C','o','p','y','F','i','l','e','s',0};
108 static const WCHAR DelFiles[]   = {'D','e','l','F','i','l','e','s',0};
109 static const WCHAR RenFiles[]   = {'R','e','n','F','i','l','e','s',0};
110 static const WCHAR Ini2Reg[]    = {'I','n','i','2','R','e','g',0};
111 static const WCHAR LogConf[]    = {'L','o','g','C','o','n','f',0};
112 static const WCHAR AddReg[]     = {'A','d','d','R','e','g',0};
113 static const WCHAR DelReg[]     = {'D','e','l','R','e','g',0};
114 static const WCHAR BitReg[]     = {'B','i','t','R','e','g',0};
115 static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
116 static const WCHAR CopyINF[]    = {'C','o','p','y','I','N','F',0};
117 static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
118 static const WCHAR RegisterDlls[]    = {'R','e','g','i','s','t','e','r','D','l','l','s',0};
119 static const WCHAR UnregisterDlls[]  = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0};
120 static const WCHAR ProfileItems[]    = {'P','r','o','f','i','l','e','I','t','e','m','s',0};
121 static const WCHAR Include[]         = {'I','n','c','l','u','d','e',0};
122 static const WCHAR Needs[]           = {'N','e','e','d','s',0};
123 static const WCHAR DotSecurity[]     = {'.','S','e','c','u','r','i','t','y',0};
124 #ifdef __WINESRC__
125 static const WCHAR WineFakeDlls[]    = {'W','i','n','e','F','a','k','e','D','l','l','s',0};
126 #endif
127 
128 
129 /***********************************************************************
130  *            get_field_string
131  *
132  * Retrieve the contents of a field, dynamically growing the buffer if necessary.
133  */
get_field_string(INFCONTEXT * context,DWORD index,WCHAR * buffer,WCHAR * static_buffer,DWORD * size)134 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
135                                 WCHAR *static_buffer, DWORD *size )
136 {
137     DWORD required;
138 
139     if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
140     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
141     {
142         /* now grow the buffer */
143         if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
144         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
145         *size = required;
146         if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
147     }
148     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
149     return NULL;
150 }
151 
152 
153 /***********************************************************************
154  *            copy_files_callback
155  *
156  * Called once for each CopyFiles entry in a given section.
157  */
copy_files_callback(HINF hinf,PCWSTR field,void * arg)158 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
159 {
160     struct files_callback_info *info = arg;
161 
162     if (field[0] == '@')  /* special case: copy single file */
163         SetupQueueDefaultCopyW( info->queue, info->layout ? info->layout : hinf, info->src_root, NULL, field+1, info->copy_flags );
164     else
165         SetupQueueCopySectionW( info->queue, info->src_root, info->layout ? info->layout : hinf, hinf, field, info->copy_flags );
166     return TRUE;
167 }
168 
169 
170 /***********************************************************************
171  *            delete_files_callback
172  *
173  * Called once for each DelFiles entry in a given section.
174  */
delete_files_callback(HINF hinf,PCWSTR field,void * arg)175 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
176 {
177     struct files_callback_info *info = arg;
178     SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
179     return TRUE;
180 }
181 
182 
183 /***********************************************************************
184  *            rename_files_callback
185  *
186  * Called once for each RenFiles entry in a given section.
187  */
rename_files_callback(HINF hinf,PCWSTR field,void * arg)188 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
189 {
190     struct files_callback_info *info = arg;
191     SetupQueueRenameSectionW( info->queue, hinf, 0, field );
192     return TRUE;
193 }
194 
195 
196 /***********************************************************************
197  *            get_root_key
198  *
199  * Retrieve the registry root key from its name.
200  */
get_root_key(const WCHAR * name,HKEY def_root)201 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
202 {
203     static const WCHAR HKCR[] = {'H','K','C','R',0};
204     static const WCHAR HKCU[] = {'H','K','C','U',0};
205     static const WCHAR HKLM[] = {'H','K','L','M',0};
206     static const WCHAR HKU[]  = {'H','K','U',0};
207     static const WCHAR HKR[]  = {'H','K','R',0};
208 
209     if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
210     if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
211     if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
212     if (!strcmpiW( name, HKU )) return HKEY_USERS;
213     if (!strcmpiW( name, HKR )) return def_root;
214     return 0;
215 }
216 
217 
218 /***********************************************************************
219  *            append_multi_sz_value
220  *
221  * Append a multisz string to a multisz registry value.
222  */
append_multi_sz_value(HKEY hkey,const WCHAR * value,const WCHAR * strings,DWORD str_size)223 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
224                                    DWORD str_size )
225 {
226     DWORD size, type, total;
227     WCHAR *buffer, *p;
228 
229     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
230     if (type != REG_MULTI_SZ) return;
231 
232     size = size + str_size * sizeof(WCHAR) ;
233     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size))) return;
234     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
235 
236     /* compare each string against all the existing ones */
237     total = size;
238     while (*strings)
239     {
240         int len = strlenW(strings) + 1;
241 
242         for (p = buffer; *p; p += strlenW(p) + 1)
243             if (!strcmpiW( p, strings )) break;
244 
245         if (!*p)  /* not found, need to append it */
246         {
247             memcpy( p, strings, len * sizeof(WCHAR) );
248             p[len] = 0;
249             total += len * sizeof(WCHAR);
250         }
251         strings += len;
252     }
253     if (total != size)
254     {
255         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
256         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total + sizeof(WCHAR) );
257     }
258  done:
259     HeapFree( GetProcessHeap(), 0, buffer );
260 }
261 
262 
263 /***********************************************************************
264  *            delete_multi_sz_value
265  *
266  * Remove a string from a multisz registry value.
267  */
delete_multi_sz_value(HKEY hkey,const WCHAR * value,const WCHAR * string)268 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
269 {
270     DWORD size, type;
271     WCHAR *buffer, *src, *dst;
272 
273     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
274     if (type != REG_MULTI_SZ) return;
275     /* allocate double the size, one for value before and one for after */
276     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2))) return;
277     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
278     src = buffer;
279     dst = buffer + size;
280     while (*src)
281     {
282         int len = strlenW(src) + 1;
283         if (strcmpiW( src, string ))
284         {
285             memcpy( dst, src, len * sizeof(WCHAR) );
286             dst += len;
287         }
288         src += len;
289     }
290     *dst++ = 0;
291     if (dst != buffer + 2*size)  /* did we remove something? */
292     {
293         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
294         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
295                         (BYTE *)(buffer + size), dst - (buffer + size) );
296     }
297  done:
298     HeapFree( GetProcessHeap(), 0, buffer );
299 }
300 
301 
302 /***********************************************************************
303  *            do_reg_operation
304  *
305  * Perform an add/delete registry operation depending on the flags.
306  */
do_reg_operation(HKEY hkey,const WCHAR * value,INFCONTEXT * context,INT flags)307 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
308 {
309     DWORD type, size;
310 
311     if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL))  /* deletion */
312     {
313         if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
314         {
315             if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
316             {
317                 WCHAR *str;
318 
319                 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
320                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
321                 SetupGetStringFieldW( context, 5, str, size, NULL );
322                 delete_multi_sz_value( hkey, value, str );
323                 HeapFree( GetProcessHeap(), 0, str );
324             }
325             else RegDeleteValueW( hkey, value );
326         }
327         else NtDeleteKey( hkey );
328         return TRUE;
329     }
330 
331     if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
332 
333     if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
334     {
335         BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
336         if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
337         if (!exists && (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
338     }
339 
340     switch(flags & FLG_ADDREG_TYPE_MASK)
341     {
342     case FLG_ADDREG_TYPE_SZ:        type = REG_SZ; break;
343     case FLG_ADDREG_TYPE_MULTI_SZ:  type = REG_MULTI_SZ; break;
344     case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
345     case FLG_ADDREG_TYPE_BINARY:    type = REG_BINARY; break;
346     case FLG_ADDREG_TYPE_DWORD:     type = REG_DWORD; break;
347     case FLG_ADDREG_TYPE_NONE:      type = REG_NONE; break;
348     default:                        type = flags >> 16; break;
349     }
350 
351     if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
352         (type == REG_DWORD && SetupGetFieldCount(context) == 5))
353     {
354         static const WCHAR empty;
355         WCHAR *str = NULL;
356 
357         if (type == REG_MULTI_SZ)
358         {
359             if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
360             if (size)
361             {
362                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
363                 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
364             }
365             if (flags & FLG_ADDREG_APPEND)
366             {
367                 if (!str) return TRUE;
368                 append_multi_sz_value( hkey, value, str, size );
369                 HeapFree( GetProcessHeap(), 0, str );
370                 return TRUE;
371             }
372             /* else fall through to normal string handling */
373         }
374         else
375         {
376             if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
377             if (size)
378             {
379                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
380                 SetupGetStringFieldW( context, 5, str, size, NULL );
381             }
382         }
383 
384         if (type == REG_DWORD)
385         {
386             DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
387             TRACE( "setting dword %s to %x\n", debugstr_w(value), dw );
388             RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
389         }
390         else
391         {
392             TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
393             if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
394             else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
395         }
396         HeapFree( GetProcessHeap(), 0, str );
397         return TRUE;
398     }
399     else  /* get the binary data */
400     {
401         BYTE *data = NULL;
402 
403         if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
404         if (size)
405         {
406             if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
407             TRACE( "setting binary data %s len %d\n", debugstr_w(value), size );
408             SetupGetBinaryField( context, 5, data, size, NULL );
409         }
410         RegSetValueExW( hkey, value, 0, type, data, size );
411         HeapFree( GetProcessHeap(), 0, data );
412         return TRUE;
413     }
414 }
415 
416 
417 /***********************************************************************
418  *            registry_callback
419  *
420  * Called once for each AddReg and DelReg entry in a given section.
421  */
registry_callback(HINF hinf,PCWSTR field,void * arg)422 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
423 {
424     struct registry_callback_info *info = arg;
425     LPWSTR security_key, security_descriptor;
426     INFCONTEXT context, security_context;
427     PSECURITY_DESCRIPTOR sd = NULL;
428     SECURITY_ATTRIBUTES security_attributes = { 0, };
429     HKEY root_key, hkey;
430     DWORD required;
431 
432     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
433     if (!ok)
434         return TRUE;
435 
436     /* Check for .Security section */
437     security_key = MyMalloc( (strlenW( field ) + strlenW( DotSecurity )) * sizeof(WCHAR) + sizeof(UNICODE_NULL) );
438     if (!security_key)
439     {
440         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
441         return FALSE;
442     }
443     strcpyW( security_key, field );
444     strcatW( security_key, DotSecurity );
445     ok = SetupFindFirstLineW( hinf, security_key, NULL, &security_context );
446     MyFree(security_key);
447     if (ok)
448     {
449         if (!SetupGetLineTextW( &security_context, NULL, NULL, NULL, NULL, 0, &required ))
450             return FALSE;
451         security_descriptor = MyMalloc( required * sizeof(WCHAR) );
452         if (!security_descriptor)
453         {
454             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
455             return FALSE;
456         }
457         if (!SetupGetLineTextW( &security_context, NULL, NULL, NULL, security_descriptor, required, NULL ))
458             return FALSE;
459         ok = ConvertStringSecurityDescriptorToSecurityDescriptorW( security_descriptor, SDDL_REVISION_1, &sd, NULL );
460         MyFree( security_descriptor );
461         if (!ok)
462             return FALSE;
463         security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
464         security_attributes.lpSecurityDescriptor = sd;
465     }
466 
467     for (ok = TRUE; ok; ok = SetupFindNextLine( &context, &context ))
468     {
469         WCHAR buffer[MAX_INF_STRING_LENGTH];
470         INT flags;
471 
472         /* get root */
473         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
474             continue;
475         if (!(root_key = get_root_key( buffer, info->default_root )))
476             continue;
477 
478         /* get key */
479         if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
480             *buffer = 0;
481 
482         /* get flags */
483         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
484 
485         if (!info->delete)
486         {
487             if (flags & FLG_ADDREG_DELREG_BIT) continue;  /* ignore this entry */
488         }
489         else
490         {
491             if (!flags) flags = FLG_ADDREG_DELREG_BIT;
492             else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue;  /* ignore this entry */
493         }
494 
495         if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
496         {
497             if (RegOpenKeyW( root_key, buffer, &hkey )) continue;  /* ignore if it doesn't exist */
498         }
499         else if (RegCreateKeyExW( root_key, buffer, 0, NULL, 0, MAXIMUM_ALLOWED,
500             sd ? &security_attributes : NULL, &hkey, NULL ))
501         {
502             ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
503             continue;
504         }
505         TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
506 
507         /* get value name */
508         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
509             *buffer = 0;
510 
511         /* and now do it */
512         if (!do_reg_operation( hkey, buffer, &context, flags ))
513         {
514             if (hkey != root_key) RegCloseKey( hkey );
515             if (sd) LocalFree( sd );
516             return FALSE;
517         }
518         if (hkey != root_key) RegCloseKey( hkey );
519     }
520     if (sd) LocalFree( sd );
521     return TRUE;
522 }
523 
524 
525 /***********************************************************************
526  *            do_register_dll
527  *
528  * Register or unregister a dll.
529  */
do_register_dll(const struct register_dll_info * info,const WCHAR * path,INT flags,INT timeout,const WCHAR * args)530 static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
531                              INT flags, INT timeout, const WCHAR *args )
532 {
533     HMODULE module;
534     HRESULT res;
535     SP_REGISTER_CONTROL_STATUSW status;
536     IMAGE_NT_HEADERS *nt;
537 
538     status.cbSize = sizeof(status);
539     status.FileName = path;
540     status.FailureCode = SPREG_SUCCESS;
541     status.Win32Error = ERROR_SUCCESS;
542 
543     if (info->callback)
544     {
545         switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
546                                (UINT_PTR)&status, !info->unregister ))
547         {
548         case FILEOP_ABORT:
549             SetLastError( ERROR_OPERATION_ABORTED );
550             return FALSE;
551         case FILEOP_SKIP:
552             return TRUE;
553         case FILEOP_DOIT:
554             break;
555         }
556     }
557 
558     if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
559     {
560         WARN( "could not load %s\n", debugstr_w(path) );
561         status.FailureCode = SPREG_LOADLIBRARY;
562         status.Win32Error = GetLastError();
563         goto done;
564     }
565 
566     if ((nt = RtlImageNtHeader( module )) && !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
567     {
568         /* file is an executable, not a dll */
569         STARTUPINFOW startup;
570         PROCESS_INFORMATION info;
571         WCHAR *cmd_line;
572         BOOL res;
573         static const WCHAR format[] = {'"','%','s','"',' ','%','s',0};
574         static const WCHAR default_args[] = {'/','R','e','g','S','e','r','v','e','r',0};
575 
576         FreeLibrary( module );
577         module = NULL;
578         if (!args) args = default_args;
579         cmd_line = HeapAlloc( GetProcessHeap(), 0, (strlenW(path) + strlenW(args) + 4) * sizeof(WCHAR) );
580         sprintfW( cmd_line, format, path, args );
581         memset( &startup, 0, sizeof(startup) );
582         startup.cb = sizeof(startup);
583         TRACE( "executing %s\n", debugstr_w(cmd_line) );
584         res = CreateProcessW( NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
585         HeapFree( GetProcessHeap(), 0, cmd_line );
586         if (!res)
587         {
588             status.FailureCode = SPREG_LOADLIBRARY;
589             status.Win32Error = GetLastError();
590             goto done;
591         }
592         CloseHandle( info.hThread );
593 
594         if (WaitForSingleObject( info.hProcess, timeout*1000 ) == WAIT_TIMEOUT)
595         {
596             /* timed out, kill the process */
597             TerminateProcess( info.hProcess, 1 );
598             status.FailureCode = SPREG_TIMEOUT;
599             status.Win32Error = ERROR_TIMEOUT;
600         }
601         CloseHandle( info.hProcess );
602         goto done;
603     }
604 
605     if (flags & FLG_REGSVR_DLLREGISTER)
606     {
607         const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
608         HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
609 
610         if (!func)
611         {
612             status.FailureCode = SPREG_GETPROCADDR;
613             status.Win32Error = GetLastError();
614             goto done;
615         }
616 
617         TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
618         res = func();
619 
620         if (FAILED(res))
621         {
622             WARN( "calling %s in %s returned error %x\n", entry_point, debugstr_w(path), res );
623             status.FailureCode = SPREG_REGSVR;
624             status.Win32Error = res;
625             goto done;
626         }
627     }
628 
629     if (flags & FLG_REGSVR_DLLINSTALL)
630     {
631         HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
632 
633         if (!func)
634         {
635             status.FailureCode = SPREG_GETPROCADDR;
636             status.Win32Error = GetLastError();
637             goto done;
638         }
639 
640         TRACE( "calling DllInstall(%d,%s) in %s\n",
641                !info->unregister, debugstr_w(args), debugstr_w(path) );
642         res = func( !info->unregister, args );
643 
644         if (FAILED(res))
645         {
646             WARN( "calling DllInstall in %s returned error %x\n", debugstr_w(path), res );
647             status.FailureCode = SPREG_REGSVR;
648             status.Win32Error = res;
649             goto done;
650         }
651     }
652 
653 done:
654     if (module) FreeLibrary( module );
655     if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
656                                         (UINT_PTR)&status, !info->unregister );
657     return TRUE;
658 }
659 
660 
661 /***********************************************************************
662  *            register_dlls_callback
663  *
664  * Called once for each RegisterDlls entry in a given section.
665  */
register_dlls_callback(HINF hinf,PCWSTR field,void * arg)666 static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
667 {
668     struct register_dll_info *info = arg;
669     INFCONTEXT context;
670     BOOL ret = TRUE;
671     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
672 
673     for (; ok; ok = SetupFindNextLine( &context, &context ))
674     {
675         WCHAR *path, *args, *p;
676         WCHAR buffer[MAX_INF_STRING_LENGTH];
677         INT flags, timeout;
678 
679         /* get directory */
680         if (!(path = PARSER_get_dest_dir( &context ))) continue;
681 
682         /* get dll name */
683         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
684             goto done;
685         if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
686                                (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
687         path = p;
688         p += strlenW(p);
689         if (p == path || p[-1] != '\\') *p++ = '\\';
690         strcpyW( p, buffer );
691 
692         /* get flags */
693         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
694 
695         /* get timeout */
696 #ifdef __REACTOS__
697         /* "11,,cmd.exe,,,/K dir" means default timeout, not a timeout of zero */
698         if (!SetupGetIntField( &context, 5, &timeout ) || timeout == 0) timeout = 60;
699 #else
700         if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
701 #endif
702 
703         /* get command line */
704         args = NULL;
705         if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
706             args = buffer;
707 
708         ret = do_register_dll( info, path, flags, timeout, args );
709 
710     done:
711         HeapFree( GetProcessHeap(), 0, path );
712         if (!ret) break;
713     }
714     return ret;
715 }
716 
717 #ifdef __WINESRC__
718 /***********************************************************************
719  *            fake_dlls_callback
720  *
721  * Called once for each WineFakeDlls entry in a given section.
722  */
fake_dlls_callback(HINF hinf,PCWSTR field,void * arg)723 static BOOL fake_dlls_callback( HINF hinf, PCWSTR field, void *arg )
724 {
725     INFCONTEXT context;
726     BOOL ret = TRUE;
727     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
728 
729     for (; ok; ok = SetupFindNextLine( &context, &context ))
730     {
731         WCHAR *path, *p;
732         WCHAR buffer[MAX_INF_STRING_LENGTH];
733 
734         /* get directory */
735         if (!(path = PARSER_get_dest_dir( &context ))) continue;
736 
737         /* get dll name */
738         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
739             goto done;
740         if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
741                                (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
742         path = p;
743         p += strlenW(p);
744         if (p == path || p[-1] != '\\') *p++ = '\\';
745         strcpyW( p, buffer );
746 
747         /* get source dll */
748         if (SetupGetStringFieldW( &context, 4, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
749             p = buffer;  /* otherwise use target base name as default source */
750 
751         create_fake_dll( path, p );  /* ignore errors */
752 
753     done:
754         HeapFree( GetProcessHeap(), 0, path );
755         if (!ret) break;
756     }
757     return ret;
758 }
759 #endif // __WINESRC__
760 
761 /***********************************************************************
762  *            update_ini_callback
763  *
764  * Called once for each UpdateInis entry in a given section.
765  */
update_ini_callback(HINF hinf,PCWSTR field,void * arg)766 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
767 {
768     INFCONTEXT context;
769 
770     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
771 
772     for (; ok; ok = SetupFindNextLine( &context, &context ))
773     {
774         WCHAR buffer[MAX_INF_STRING_LENGTH];
775         WCHAR  filename[MAX_INF_STRING_LENGTH];
776         WCHAR  section[MAX_INF_STRING_LENGTH];
777         WCHAR  entry[MAX_INF_STRING_LENGTH];
778         WCHAR  string[MAX_INF_STRING_LENGTH];
779         LPWSTR divider;
780 
781         if (!SetupGetStringFieldW( &context, 1, filename,
782                                    sizeof(filename)/sizeof(WCHAR), NULL ))
783             continue;
784 
785         if (!SetupGetStringFieldW( &context, 2, section,
786                                    sizeof(section)/sizeof(WCHAR), NULL ))
787             continue;
788 
789         if (!SetupGetStringFieldW( &context, 4, buffer,
790                                    sizeof(buffer)/sizeof(WCHAR), NULL ))
791             continue;
792 
793         divider = strchrW(buffer,'=');
794         if (divider)
795         {
796             *divider = 0;
797             strcpyW(entry,buffer);
798             divider++;
799             strcpyW(string,divider);
800         }
801         else
802         {
803             strcpyW(entry,buffer);
804             string[0]=0;
805         }
806 
807         TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
808                debugstr_w(string),debugstr_w(section),debugstr_w(filename));
809         WritePrivateProfileStringW(section,entry,string,filename);
810 
811     }
812     return TRUE;
813 }
814 
update_ini_fields_callback(HINF hinf,PCWSTR field,void * arg)815 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
816 {
817     FIXME( "should update ini fields %s\n", debugstr_w(field) );
818     return TRUE;
819 }
820 
ini2reg_callback(HINF hinf,PCWSTR field,void * arg)821 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
822 {
823     FIXME( "should do ini2reg %s\n", debugstr_w(field) );
824     return TRUE;
825 }
826 
logconf_callback(HINF hinf,PCWSTR field,void * arg)827 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
828 {
829     FIXME( "should do logconf %s\n", debugstr_w(field) );
830     return TRUE;
831 }
832 
bitreg_callback(HINF hinf,PCWSTR field,void * arg)833 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
834 {
835     FIXME( "should do bitreg %s\n", debugstr_w(field) );
836     return TRUE;
837 }
838 
Concatenate(int DirId,LPCWSTR SubDirPart,LPCWSTR NamePart,LPWSTR * pFullName)839 static BOOL Concatenate(int DirId, LPCWSTR SubDirPart, LPCWSTR NamePart, LPWSTR *pFullName)
840 {
841     DWORD dwRequired = 0;
842     LPCWSTR Dir;
843     LPWSTR FullName;
844 
845     *pFullName = NULL;
846 
847     Dir = DIRID_get_string(DirId);
848     if (Dir && *Dir)
849         dwRequired += wcslen(Dir) + 1;
850     else
851         Dir = NULL; /* DIRID_get_string returns L"" for DIRID_ABSOLUTE */
852     if (SubDirPart)
853         dwRequired += wcslen(SubDirPart) + 1;
854     if (NamePart)
855         dwRequired += wcslen(NamePart);
856     dwRequired = dwRequired * sizeof(WCHAR) + sizeof(UNICODE_NULL);
857 
858     FullName = MyMalloc(dwRequired);
859     if (!FullName)
860     {
861         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
862         return FALSE;
863     }
864     FullName[0] = UNICODE_NULL;
865 
866     if (Dir)
867     {
868         wcscat(FullName, Dir);
869         if (FullName[wcslen(FullName) - 1] != '\\')
870             wcscat(FullName, BackSlash);
871     }
872     if (SubDirPart)
873     {
874         wcscat(FullName, SubDirPart);
875         if (FullName[wcslen(FullName) - 1] != '\\')
876             wcscat(FullName, BackSlash);
877     }
878     if (NamePart)
879         wcscat(FullName, NamePart);
880 
881     *pFullName = FullName;
882     return TRUE;
883 }
884 
885 /***********************************************************************
886  *            profile_items_callback
887  *
888  * Called once for each ProfileItems entry in a given section.
889  */
890 static BOOL
profile_items_callback(IN HINF hInf,IN PCWSTR SectionName,IN PVOID Arg)891 profile_items_callback(
892     IN HINF hInf,
893     IN PCWSTR SectionName,
894     IN PVOID Arg)
895 {
896     INFCONTEXT Context;
897     LPWSTR LinkSubDir = NULL, LinkName = NULL;
898     INT LinkAttributes = 0;
899     INT LinkFolder = 0;
900     INT FileDirId = 0;
901     INT CSIDL = CSIDL_COMMON_PROGRAMS;
902     LPWSTR FileSubDir = NULL;
903     INT DirId = 0;
904     LPWSTR SubDirPart = NULL, NamePart = NULL;
905     LPWSTR FullLinkName = NULL, FullFileName = NULL, FullWorkingDir = NULL, FullIconName = NULL;
906     INT IconIdx = 0;
907     LPWSTR lpHotKey = NULL, lpInfoTip = NULL;
908     LPWSTR DisplayName = NULL;
909     INT DisplayResId = 0;
910     BOOL ret = FALSE;
911     DWORD Index, Required;
912 
913     IShellLinkW *psl;
914     IPersistFile *ppf;
915     HMODULE hOle32 = NULL;
916     COINITIALIZE pCoInitialize;
917     COCREATEINSTANCE pCoCreateInstance;
918     COUNINITIALIZE pCoUninitialize;
919     HRESULT hr;
920 
921     TRACE("hInf %p, SectionName %s, Arg %p\n",
922         hInf, debugstr_w(SectionName), Arg);
923 
924     /* Read 'Name' entry */
925     if (!SetupFindFirstLineW(hInf, SectionName, Name, &Context))
926         goto cleanup;
927     if (!GetStringField(&Context, 1, &LinkName))
928         goto cleanup;
929     if (SetupGetFieldCount(&Context) >= 2)
930     {
931         if (!SetupGetIntField(&Context, 2, &LinkAttributes))
932             goto cleanup;
933     }
934     if (SetupGetFieldCount(&Context) >= 3)
935     {
936         if (!SetupGetIntField(&Context, 3, &LinkFolder))
937             goto cleanup;
938     }
939 
940     /* Read 'CmdLine' entry */
941     if (!SetupFindFirstLineW(hInf, SectionName, CmdLine, &Context))
942         goto cleanup;
943     Index = 1;
944     if (!SetupGetIntField(&Context, Index++, &FileDirId))
945         goto cleanup;
946     if (SetupGetFieldCount(&Context) >= 3)
947     {
948         if (!GetStringField(&Context, Index++, &FileSubDir))
949             goto cleanup;
950     }
951     if (!GetStringField(&Context, Index++, &NamePart))
952         goto cleanup;
953     if (!Concatenate(FileDirId, FileSubDir, NamePart, &FullFileName))
954         goto cleanup;
955     MyFree(NamePart);
956     NamePart = NULL;
957 
958     /* Read 'SubDir' entry */
959     if ((LinkAttributes & FLG_PROFITEM_GROUP) == 0 && SetupFindFirstLineW(hInf, SectionName, SubDir, &Context))
960     {
961         if (!GetStringField(&Context, 1, &LinkSubDir))
962             goto cleanup;
963     }
964 
965     /* Read 'WorkingDir' entry */
966     if (SetupFindFirstLineW(hInf, SectionName, WorkingDir, &Context))
967     {
968         if (!SetupGetIntField(&Context, 1, &DirId))
969             goto cleanup;
970         if (SetupGetFieldCount(&Context) >= 2)
971         {
972             if (!GetStringField(&Context, 2, &SubDirPart))
973                 goto cleanup;
974         }
975         if (!Concatenate(DirId, SubDirPart, NULL, &FullWorkingDir))
976             goto cleanup;
977         MyFree(SubDirPart);
978         SubDirPart = NULL;
979     }
980     else
981     {
982         if (!Concatenate(FileDirId, FileSubDir, NULL, &FullWorkingDir))
983             goto cleanup;
984     }
985 
986     /* Read 'IconPath' entry */
987     if (SetupFindFirstLineW(hInf, SectionName, IconPath, &Context))
988     {
989         Index = 1;
990         if (!SetupGetIntField(&Context, Index++, &DirId))
991             goto cleanup;
992         if (SetupGetFieldCount(&Context) >= 3)
993         {
994             if (!GetStringField(&Context, Index++, &SubDirPart))
995                 goto cleanup;
996         }
997         if (!GetStringField(&Context, Index, &NamePart))
998             goto cleanup;
999         if (!Concatenate(DirId, SubDirPart, NamePart, &FullIconName))
1000             goto cleanup;
1001         MyFree(SubDirPart);
1002         MyFree(NamePart);
1003         SubDirPart = NamePart = NULL;
1004     }
1005     else
1006     {
1007         FullIconName = pSetupDuplicateString(FullFileName);
1008         if (!FullIconName)
1009             goto cleanup;
1010     }
1011 
1012     /* Read 'IconIndex' entry */
1013     if (SetupFindFirstLineW(hInf, SectionName, IconIndex, &Context))
1014     {
1015         if (!SetupGetIntField(&Context, 1, &IconIdx))
1016             goto cleanup;
1017     }
1018 
1019     /* Read 'HotKey' and 'InfoTip' entries */
1020     GetLineText(hInf, SectionName, HotKey, &lpHotKey);
1021     GetLineText(hInf, SectionName, InfoTip, &lpInfoTip);
1022 
1023     /* Read 'DisplayResource' entry */
1024     if (SetupFindFirstLineW(hInf, SectionName, DisplayResource, &Context))
1025     {
1026         if (!GetStringField(&Context, 1, &DisplayName))
1027             goto cleanup;
1028         if (!SetupGetIntField(&Context, 2, &DisplayResId))
1029             goto cleanup;
1030     }
1031 
1032     /* Some debug */
1033     TRACE("Link is %s\\%s, attributes 0x%x\n", debugstr_w(LinkSubDir), debugstr_w(LinkName), LinkAttributes);
1034     TRACE("File is %s\n", debugstr_w(FullFileName));
1035     TRACE("Working dir %s\n", debugstr_w(FullWorkingDir));
1036     TRACE("Icon is %s, %d\n", debugstr_w(FullIconName), IconIdx);
1037     TRACE("Hotkey %s\n", debugstr_w(lpHotKey));
1038     TRACE("InfoTip %s\n", debugstr_w(lpInfoTip));
1039     TRACE("Display %s, %d\n", DisplayName, DisplayResId);
1040 
1041     /* Load ole32.dll */
1042     hOle32 = LoadLibraryA("ole32.dll");
1043     if (!hOle32)
1044         goto cleanup;
1045     pCoInitialize = (COINITIALIZE)GetProcAddress(hOle32, "CoInitialize");
1046     if (!pCoInitialize)
1047         goto cleanup;
1048     pCoCreateInstance = (COCREATEINSTANCE)GetProcAddress(hOle32, "CoCreateInstance");
1049     if (!pCoCreateInstance)
1050         goto cleanup;
1051     pCoUninitialize = (COUNINITIALIZE)GetProcAddress(hOle32, "CoUninitialize");
1052     if (!pCoUninitialize)
1053         goto cleanup;
1054 
1055     /* Create shortcut */
1056     hr = pCoInitialize(NULL);
1057     if (!SUCCEEDED(hr))
1058     {
1059         if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
1060             SetLastError(HRESULT_CODE(hr));
1061         else
1062             SetLastError(E_FAIL);
1063         goto cleanup;
1064     }
1065     hr = pCoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&psl);
1066     if (SUCCEEDED(hr))
1067     {
1068         /* Fill link properties */
1069         hr = IShellLinkW_SetPath(psl, FullFileName);
1070         if (SUCCEEDED(hr))
1071             hr = IShellLinkW_SetArguments(psl, L"");
1072         if (SUCCEEDED(hr))
1073             hr = IShellLinkW_SetWorkingDirectory(psl, FullWorkingDir);
1074         if (SUCCEEDED(hr))
1075             hr = IShellLinkW_SetIconLocation(psl, FullIconName, IconIdx);
1076         if (SUCCEEDED(hr) && lpHotKey)
1077             FIXME("Need to store hotkey %s in shell link\n", debugstr_w(lpHotKey));
1078         if (SUCCEEDED(hr) && lpInfoTip)
1079             hr = IShellLinkW_SetDescription(psl, lpInfoTip);
1080         if (SUCCEEDED(hr) && DisplayName)
1081             FIXME("Need to store display name %s, %d in shell link\n", debugstr_w(DisplayName), DisplayResId);
1082         if (SUCCEEDED(hr))
1083         {
1084             hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1085             if (SUCCEEDED(hr))
1086             {
1087                 Required = (MAX_PATH + 1 +
1088                            ((LinkSubDir != NULL) ? wcslen(LinkSubDir) : 0) +
1089                            ((LinkName != NULL) ? wcslen(LinkName) : 0)) * sizeof(WCHAR);
1090                 FullLinkName = MyMalloc(Required);
1091                 if (!FullLinkName)
1092                     hr = E_OUTOFMEMORY;
1093                 else
1094                 {
1095                     if (LinkAttributes & (FLG_PROFITEM_DELETE | FLG_PROFITEM_GROUP))
1096                         FIXME("Need to handle FLG_PROFITEM_DELETE and FLG_PROFITEM_GROUP\n");
1097                     if (LinkAttributes & FLG_PROFITEM_CSIDL)
1098                         CSIDL = LinkFolder;
1099                     else if (LinkAttributes & FLG_PROFITEM_CURRENTUSER)
1100                         CSIDL = CSIDL_PROGRAMS;
1101 
1102                     if (SHGetSpecialFolderPathW(
1103                         NULL,
1104                         FullLinkName,
1105                         CSIDL,
1106                         TRUE))
1107                     {
1108                         if (FullLinkName[wcslen(FullLinkName) - 1] != '\\')
1109                             wcscat(FullLinkName, BackSlash);
1110                         if (LinkSubDir)
1111                         {
1112                             wcscat(FullLinkName, LinkSubDir);
1113                             if (FullLinkName[wcslen(FullLinkName) - 1] != '\\')
1114                                 wcscat(FullLinkName, BackSlash);
1115                         }
1116                         if (LinkName)
1117                         {
1118                             wcscat(FullLinkName, LinkName);
1119                             wcscat(FullLinkName, DotLnk);
1120                         }
1121                         hr = IPersistFile_Save(ppf, FullLinkName, TRUE);
1122                     }
1123                     else
1124                         hr = HRESULT_FROM_WIN32(GetLastError());
1125                 }
1126                 IPersistFile_Release(ppf);
1127             }
1128         }
1129         IShellLinkW_Release(psl);
1130     }
1131     pCoUninitialize();
1132     if (SUCCEEDED(hr))
1133         ret = TRUE;
1134     else
1135     {
1136         if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
1137             SetLastError(HRESULT_CODE(hr));
1138         else
1139             SetLastError(E_FAIL);
1140     }
1141 
1142 cleanup:
1143     MyFree(LinkSubDir);
1144     MyFree(LinkName);
1145     MyFree(FileSubDir);
1146     MyFree(SubDirPart);
1147     MyFree(NamePart);
1148     MyFree(FullFileName);
1149     MyFree(FullWorkingDir);
1150     MyFree(FullIconName);
1151     MyFree(FullLinkName);
1152     MyFree(lpHotKey);
1153     MyFree(lpInfoTip);
1154     MyFree(DisplayName);
1155     if (hOle32)
1156         FreeLibrary(hOle32);
1157 
1158     TRACE("Returning %d\n", ret);
1159     return ret;
1160 }
1161 
copy_inf_callback(HINF hinf,PCWSTR field,void * arg)1162 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
1163 {
1164     FIXME( "should do copy inf %s\n", debugstr_w(field) );
1165     return TRUE;
1166 }
1167 
1168 
1169 /***********************************************************************
1170  *            iterate_section_fields
1171  *
1172  * Iterate over all fields of a certain key of a certain section
1173  */
iterate_section_fields(HINF hinf,PCWSTR section,PCWSTR key,iterate_fields_func callback,void * arg)1174 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
1175                                     iterate_fields_func callback, void *arg )
1176 {
1177     WCHAR static_buffer[200];
1178     WCHAR *buffer = static_buffer;
1179     DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
1180     INFCONTEXT context;
1181     BOOL ret = FALSE;
1182 
1183     BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
1184     while (ok)
1185     {
1186         UINT i, count = SetupGetFieldCount( &context );
1187         for (i = 1; i <= count; i++)
1188         {
1189             if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
1190                 goto done;
1191             if (!callback( hinf, buffer, arg ))
1192             {
1193                 WARN("callback failed for %s %s err %d\n",
1194                      debugstr_w(section), debugstr_w(buffer), GetLastError() );
1195                 goto done;
1196             }
1197         }
1198         ok = SetupFindNextMatchLineW( &context, key, &context );
1199     }
1200     ret = TRUE;
1201  done:
1202     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
1203     return ret;
1204 }
1205 
1206 
1207 /***********************************************************************
1208  *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
1209  */
SetupInstallFilesFromInfSectionA(HINF hinf,HINF hlayout,HSPFILEQ queue,PCSTR section,PCSTR src_root,UINT flags)1210 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
1211                                               PCSTR section, PCSTR src_root, UINT flags )
1212 {
1213     UNICODE_STRING sectionW;
1214     BOOL ret = FALSE;
1215 
1216     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1217     {
1218         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1219         return FALSE;
1220     }
1221     if (!src_root)
1222         ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
1223                                                 NULL, flags );
1224     else
1225     {
1226         UNICODE_STRING srcW;
1227         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
1228         {
1229             ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
1230                                                     srcW.Buffer, flags );
1231             RtlFreeUnicodeString( &srcW );
1232         }
1233         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1234     }
1235     RtlFreeUnicodeString( &sectionW );
1236     return ret;
1237 }
1238 
1239 
1240 /***********************************************************************
1241  *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
1242  */
SetupInstallFilesFromInfSectionW(HINF hinf,HINF hlayout,HSPFILEQ queue,PCWSTR section,PCWSTR src_root,UINT flags)1243 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
1244                                               PCWSTR section, PCWSTR src_root, UINT flags )
1245 {
1246     struct files_callback_info info;
1247 
1248     info.queue      = queue;
1249     info.src_root   = src_root;
1250     info.copy_flags = flags;
1251     info.layout     = hlayout;
1252     return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
1253 }
1254 
1255 
1256 /***********************************************************************
1257  *            SetupInstallFromInfSectionA   (SETUPAPI.@)
1258  */
SetupInstallFromInfSectionA(HWND owner,HINF hinf,PCSTR section,UINT flags,HKEY key_root,PCSTR src_root,UINT copy_flags,PSP_FILE_CALLBACK_A callback,PVOID context,HDEVINFO devinfo,PSP_DEVINFO_DATA devinfo_data)1259 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
1260                                          HKEY key_root, PCSTR src_root, UINT copy_flags,
1261                                          PSP_FILE_CALLBACK_A callback, PVOID context,
1262                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1263 {
1264     UNICODE_STRING sectionW, src_rootW;
1265     struct callback_WtoA_context ctx;
1266     BOOL ret = FALSE;
1267 
1268     src_rootW.Buffer = NULL;
1269     if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
1270     {
1271         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1272         return FALSE;
1273     }
1274 
1275     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1276     {
1277         ctx.orig_context = context;
1278         ctx.orig_handler = callback;
1279         ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
1280                                            src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
1281                                            &ctx, devinfo, devinfo_data );
1282         RtlFreeUnicodeString( &sectionW );
1283     }
1284     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1285 
1286     RtlFreeUnicodeString( &src_rootW );
1287     return ret;
1288 }
1289 
1290 
1291 /***********************************************************************
1292  *            include_callback
1293  *
1294  * Called once for each Include entry in a given section.
1295  */
include_callback(HINF hinf,PCWSTR field,void * arg)1296 static BOOL include_callback( HINF hinf, PCWSTR field, void *arg )
1297 {
1298     return SetupOpenAppendInfFileW( field, hinf, NULL );
1299 }
1300 
1301 
1302 /***********************************************************************
1303  *            needs_callback
1304  *
1305  * Called once for each Needs entry in a given section.
1306  */
needs_callback(HINF hinf,PCWSTR field,void * arg)1307 static BOOL needs_callback( HINF hinf, PCWSTR field, void *arg )
1308 {
1309     struct needs_callback_info *info = arg;
1310 
1311     switch (info->type)
1312     {
1313         case 0:
1314             return SetupInstallFromInfSectionW(info->owner, *(HINF*)hinf, field, info->flags,
1315                info->key_root, info->src_root, info->copy_flags, info->callback,
1316                info->context, info->devinfo, info->devinfo_data);
1317         case 1:
1318             return SetupInstallServicesFromInfSectionExW(*(HINF*)hinf, field, info->flags,
1319                 info->devinfo, info->devinfo_data, info->reserved1, info->reserved2);
1320         default:
1321             ERR("Unknown info type %u\n", info->type);
1322             return FALSE;
1323     }
1324 }
1325 
1326 
1327 /***********************************************************************
1328  *            SetupInstallFromInfSectionW   (SETUPAPI.@)
1329  */
SetupInstallFromInfSectionW(HWND owner,HINF hinf,PCWSTR section,UINT flags,HKEY key_root,PCWSTR src_root,UINT copy_flags,PSP_FILE_CALLBACK_W callback,PVOID context,HDEVINFO devinfo,PSP_DEVINFO_DATA devinfo_data)1330 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
1331                                          HKEY key_root, PCWSTR src_root, UINT copy_flags,
1332                                          PSP_FILE_CALLBACK_W callback, PVOID context,
1333                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1334 {
1335     struct needs_callback_info needs_info;
1336 
1337     /* Parse 'Include' and 'Needs' directives */
1338     iterate_section_fields( hinf, section, Include, include_callback, NULL);
1339     needs_info.type = 0;
1340     needs_info.owner = owner;
1341     needs_info.flags = flags;
1342     needs_info.key_root = key_root;
1343     needs_info.src_root = src_root;
1344     needs_info.copy_flags = copy_flags;
1345     needs_info.callback = callback;
1346     needs_info.context = context;
1347     needs_info.devinfo = devinfo;
1348     needs_info.devinfo_data = devinfo_data;
1349     iterate_section_fields( hinf, section, Needs, needs_callback, &needs_info);
1350 
1351     if (flags & SPINST_FILES)
1352     {
1353         SP_DEVINSTALL_PARAMS_W install_params;
1354         struct files_callback_info info;
1355         HSPFILEQ queue = NULL;
1356         BOOL use_custom_queue;
1357         BOOL ret;
1358 
1359         install_params.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
1360         use_custom_queue = SetupDiGetDeviceInstallParamsW(devinfo, devinfo_data, &install_params) && (install_params.Flags & DI_NOVCP);
1361         if (!use_custom_queue && ((queue = SetupOpenFileQueue()) == (HSPFILEQ)INVALID_HANDLE_VALUE ))
1362             return FALSE;
1363         info.queue      = use_custom_queue ? install_params.FileQueue : queue;
1364         info.src_root   = src_root;
1365         info.copy_flags = copy_flags;
1366         info.layout     = hinf;
1367         ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
1368                iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
1369                iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ));
1370         if (!use_custom_queue)
1371         {
1372             if (ret)
1373                 ret = SetupCommitFileQueueW( owner, queue, callback, context );
1374             SetupCloseFileQueue( queue );
1375         }
1376         if (!ret) return FALSE;
1377     }
1378     if (flags & SPINST_INIFILES)
1379     {
1380         if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
1381             !iterate_section_fields( hinf, section, UpdateIniFields,
1382                                      update_ini_fields_callback, NULL ))
1383             return FALSE;
1384     }
1385     if (flags & SPINST_INI2REG)
1386     {
1387         if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
1388             return FALSE;
1389     }
1390     if (flags & SPINST_LOGCONFIG)
1391     {
1392         if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
1393             return FALSE;
1394     }
1395     if (flags & SPINST_REGSVR)
1396     {
1397         struct register_dll_info info;
1398 
1399         info.unregister = FALSE;
1400         if (flags & SPINST_REGISTERCALLBACKAWARE)
1401         {
1402             info.callback         = callback;
1403             info.callback_context = context;
1404         }
1405         else info.callback = NULL;
1406 
1407         if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
1408             return FALSE;
1409 
1410 #ifdef __WINESRC__
1411         if (!iterate_section_fields( hinf, section, WineFakeDlls, fake_dlls_callback, NULL ))
1412             return FALSE;
1413 #endif // __WINESRC__
1414     }
1415     if (flags & SPINST_UNREGSVR)
1416     {
1417         struct register_dll_info info;
1418 
1419         info.unregister = TRUE;
1420         if (flags & SPINST_REGISTERCALLBACKAWARE)
1421         {
1422             info.callback         = callback;
1423             info.callback_context = context;
1424         }
1425         else info.callback = NULL;
1426 
1427         if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
1428             return FALSE;
1429     }
1430     if (flags & SPINST_REGISTRY)
1431     {
1432         struct registry_callback_info info;
1433 
1434         info.default_root = key_root;
1435         info.delete = TRUE;
1436         if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
1437             return FALSE;
1438         info.delete = FALSE;
1439         if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
1440             return FALSE;
1441     }
1442     if (flags & SPINST_BITREG)
1443     {
1444         if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
1445             return FALSE;
1446     }
1447     if (flags & SPINST_PROFILEITEMS)
1448     {
1449         if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
1450             return FALSE;
1451     }
1452     if (flags & SPINST_COPYINF)
1453     {
1454         if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
1455             return FALSE;
1456     }
1457 
1458     return TRUE;
1459 }
1460 
1461 
1462 /***********************************************************************
1463  *		InstallHinfSectionW  (SETUPAPI.@)
1464  *
1465  * NOTE: 'cmdline' is <section> <mode> <path> from
1466  *   RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
1467  */
InstallHinfSectionW(HWND hwnd,HINSTANCE handle,LPCWSTR cmdline,INT show)1468 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
1469 {
1470     BOOL ret = FALSE;
1471     WCHAR *s, *path, section[MAX_PATH];
1472     void *callback_context = NULL;
1473     DWORD SectionNameLength;
1474     UINT mode;
1475     HINF hinf = INVALID_HANDLE_VALUE;
1476     BOOL bRebootRequired = FALSE;
1477 
1478     TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
1479 
1480     lstrcpynW( section, cmdline, MAX_PATH );
1481 
1482     if (!(s = strchrW( section, ' ' ))) goto cleanup;
1483     *s++ = 0;
1484     while (*s == ' ') s++;
1485     mode = atoiW( s );
1486 
1487     /* quoted paths are not allowed on native, the rest of the command line is taken as the path */
1488     if (!(s = strchrW( s, ' ' ))) goto cleanup;
1489     while (*s == ' ') s++;
1490     path = s;
1491 
1492     if (mode & 0x80)
1493     {
1494         FIXME("default path of the installation not changed\n");
1495         mode &= ~0x80;
1496     }
1497 
1498     hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
1499     if (hinf == INVALID_HANDLE_VALUE)
1500     {
1501         WARN("SetupOpenInfFileW(%s) failed (Error %u)\n", path, GetLastError());
1502         goto cleanup;
1503     }
1504 
1505     ret = SetupDiGetActualSectionToInstallW(
1506        hinf, section, section, sizeof(section)/sizeof(section[0]), &SectionNameLength, NULL );
1507     if (!ret)
1508     {
1509         WARN("SetupDiGetActualSectionToInstallW() failed (Error %u)\n", GetLastError());
1510         goto cleanup;
1511     }
1512     if (SectionNameLength > MAX_PATH - strlenW(DotServices))
1513     {
1514         WARN("Section name '%s' too long\n", section);
1515         goto cleanup;
1516     }
1517 
1518     /* Copy files and add registry entries */
1519     callback_context = SetupInitDefaultQueueCallback( hwnd );
1520     ret = SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL,
1521                                        SP_COPY_NEWER | SP_COPY_IN_USE_NEEDS_REBOOT,
1522                                        SetupDefaultQueueCallbackW, callback_context,
1523                                        NULL, NULL );
1524     if (!ret)
1525     {
1526         WARN("SetupInstallFromInfSectionW() failed (Error %u)\n", GetLastError());
1527         goto cleanup;
1528     }
1529     /* FIXME: need to check if some files were in use and need reboot
1530      * bReboot = ...;
1531      */
1532 
1533     /* Install services */
1534     wcscat(section, DotServices);
1535     ret = SetupInstallServicesFromInfSectionW( hinf, section, 0 );
1536     if (!ret && GetLastError() == ERROR_SECTION_NOT_FOUND)
1537         ret = TRUE;
1538     if (!ret)
1539     {
1540         WARN("SetupInstallServicesFromInfSectionW() failed (Error %u)\n", GetLastError());
1541         goto cleanup;
1542     }
1543     else if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED)
1544     {
1545         bRebootRequired = TRUE;
1546     }
1547 
1548     /* Check if we need to reboot */
1549     switch (mode)
1550     {
1551         case 0:
1552             /* Never reboot */
1553             break;
1554         case 1:
1555             /* Always reboot */
1556             ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
1557                 SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED);
1558             break;
1559         case 2:
1560             /* Query user before rebooting */
1561             SetupPromptReboot(NULL, hwnd, FALSE);
1562             break;
1563         case 3:
1564             /* Reboot if necessary */
1565             if (bRebootRequired)
1566             {
1567                 ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
1568                     SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED);
1569             }
1570             break;
1571         case 4:
1572             /* If necessary, query user before rebooting */
1573             if (bRebootRequired)
1574             {
1575                 SetupPromptReboot(NULL, hwnd, FALSE);
1576             }
1577             break;
1578         default:
1579             break;
1580     }
1581 
1582 cleanup:
1583     if ( callback_context )
1584         SetupTermDefaultQueueCallback( callback_context );
1585     if ( hinf != INVALID_HANDLE_VALUE )
1586         SetupCloseInfFile( hinf );
1587 
1588 #ifdef CORE_11689_IS_FIXED
1589     // TODO: Localize the error string.
1590     if (!ret && !(GlobalSetupFlags & PSPGF_NONINTERACTIVE))
1591     {
1592         MessageBoxW(hwnd, section, L"setupapi.dll: An error happened...", MB_ICONERROR | MB_OK);
1593     }
1594 #endif
1595 }
1596 
1597 
1598 /***********************************************************************
1599  *		InstallHinfSectionA  (SETUPAPI.@)
1600  */
InstallHinfSectionA(HWND hwnd,HINSTANCE handle,LPCSTR cmdline,INT show)1601 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
1602 {
1603     UNICODE_STRING cmdlineW;
1604 
1605     if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1606     {
1607         InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1608         RtlFreeUnicodeString( &cmdlineW );
1609     }
1610 }
1611 
1612 /***********************************************************************
1613  *              SetupInstallServicesFromInfSectionW  (SETUPAPI.@)
1614  */
SetupInstallServicesFromInfSectionW(HINF Inf,PCWSTR SectionName,DWORD Flags)1615 BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF Inf, PCWSTR SectionName, DWORD Flags)
1616 {
1617     return SetupInstallServicesFromInfSectionExW( Inf, SectionName, Flags,
1618                                                   NULL, NULL, NULL, NULL );
1619 }
1620 
1621 /***********************************************************************
1622  *              SetupInstallServicesFromInfSectionA  (SETUPAPI.@)
1623  */
SetupInstallServicesFromInfSectionA(HINF Inf,PCSTR SectionName,DWORD Flags)1624 BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF Inf, PCSTR SectionName, DWORD Flags)
1625 {
1626     return SetupInstallServicesFromInfSectionExA( Inf, SectionName, Flags,
1627                                                   NULL, NULL, NULL, NULL );
1628 }
1629 
1630 /***********************************************************************
1631  *		SetupInstallServicesFromInfSectionExA  (SETUPAPI.@)
1632  */
SetupInstallServicesFromInfSectionExA(HINF hinf,PCSTR sectionname,DWORD flags,HDEVINFO devinfo,PSP_DEVINFO_DATA devinfo_data,PVOID reserved1,PVOID reserved2)1633 BOOL WINAPI SetupInstallServicesFromInfSectionExA( HINF hinf, PCSTR sectionname, DWORD flags, HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data, PVOID reserved1, PVOID reserved2 )
1634 {
1635     UNICODE_STRING sectionnameW;
1636     BOOL ret = FALSE;
1637 
1638     if (RtlCreateUnicodeStringFromAsciiz( &sectionnameW, sectionname ))
1639     {
1640         ret = SetupInstallServicesFromInfSectionExW( hinf, sectionnameW.Buffer, flags, devinfo, devinfo_data, reserved1, reserved2 );
1641         RtlFreeUnicodeString( &sectionnameW );
1642     }
1643     else
1644         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1645 
1646     return ret;
1647 }
1648 
1649 
GetLineText(HINF hinf,PCWSTR section_name,PCWSTR key_name,PWSTR * value)1650 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value)
1651 {
1652     DWORD required;
1653     PWSTR buf = NULL;
1654 
1655     *value = NULL;
1656 
1657     if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, NULL, 0, &required )
1658         && GetLastError() != ERROR_INSUFFICIENT_BUFFER )
1659         return FALSE;
1660 
1661     buf = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) );
1662     if ( ! buf )
1663     {
1664         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1665         return FALSE;
1666     }
1667 
1668     if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, buf, required, &required ) )
1669     {
1670         HeapFree( GetProcessHeap(), 0, buf );
1671         return FALSE;
1672     }
1673 
1674     *value = buf;
1675     return TRUE;
1676 }
1677 
1678 
GetIntField(HINF hinf,PCWSTR section_name,PCWSTR key_name,INT * value)1679 static BOOL GetIntField( HINF hinf, PCWSTR section_name, PCWSTR key_name, INT *value)
1680 {
1681     LPWSTR buffer, end;
1682     INT res;
1683 
1684     if (! GetLineText( hinf, section_name, key_name, &buffer ) )
1685         return FALSE;
1686 
1687     res = wcstol( buffer, &end, 0 );
1688     if (end != buffer && !*end)
1689     {
1690         HeapFree(GetProcessHeap(), 0, buffer);
1691         *value = res;
1692         return TRUE;
1693     }
1694     else
1695     {
1696         HeapFree(GetProcessHeap(), 0, buffer);
1697         SetLastError( ERROR_INVALID_DATA );
1698         return FALSE;
1699     }
1700 }
1701 
1702 
GetStringField(PINFCONTEXT context,DWORD index,PWSTR * value)1703 BOOL GetStringField( PINFCONTEXT context, DWORD index, PWSTR *value)
1704 {
1705     DWORD RequiredSize;
1706     BOOL ret;
1707 
1708     ret = SetupGetStringFieldW(
1709         context,
1710         index,
1711         NULL, 0,
1712         &RequiredSize);
1713     if (!ret)
1714         return FALSE;
1715     else if (RequiredSize == 0)
1716     {
1717         *value = NULL;
1718         return TRUE;
1719     }
1720 
1721     /* We got the needed size for the buffer */
1722     *value = MyMalloc(RequiredSize * sizeof(WCHAR));
1723     if (!*value)
1724     {
1725         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1726         return FALSE;
1727     }
1728     ret = SetupGetStringFieldW(
1729         context,
1730         index,
1731         *value, RequiredSize, NULL);
1732     if (!ret)
1733         MyFree(*value);
1734 
1735     return ret;
1736 }
1737 
FixupServiceBinaryPath(IN DWORD ServiceType,IN OUT LPWSTR * ServiceBinary)1738 static VOID FixupServiceBinaryPath(
1739     IN DWORD ServiceType,
1740     IN OUT LPWSTR *ServiceBinary)
1741 {
1742     LPWSTR Buffer;
1743     WCHAR ReactOSDir[MAX_PATH];
1744     DWORD RosDirLength, ServiceLength, Win32Length;
1745 
1746     GetWindowsDirectoryW(ReactOSDir, MAX_PATH);
1747     RosDirLength = strlenW(ReactOSDir);
1748     ServiceLength = strlenW(*ServiceBinary);
1749 
1750     /* Check and fix two things:
1751        1. Get rid of C:\ReactOS and use relative
1752           path instead.
1753        2. Add %SystemRoot% for Win32 services */
1754 
1755     if (ServiceLength < RosDirLength)
1756         return;
1757 
1758     if (!wcsnicmp(*ServiceBinary, ReactOSDir, RosDirLength))
1759     {
1760         /* Yes, the first part is the C:\ReactOS\, just skip it */
1761         MoveMemory(*ServiceBinary, *ServiceBinary + RosDirLength + 1,
1762             (ServiceLength - RosDirLength) * sizeof(WCHAR));
1763 
1764         /* Handle Win32-services differently */
1765         if (ServiceType & SERVICE_WIN32)
1766         {
1767             Win32Length = (ServiceLength - RosDirLength) * sizeof(WCHAR)
1768                         - sizeof(L'\\') + sizeof(L"%SystemRoot%\\");
1769             Buffer = MyMalloc(Win32Length);
1770 
1771             wcscpy(Buffer, L"%SystemRoot%\\");
1772             wcscat(Buffer, *ServiceBinary);
1773             MyFree(*ServiceBinary);
1774 
1775             *ServiceBinary = Buffer;
1776         }
1777     }
1778 }
1779 
InstallOneService(struct DeviceInfoSet * list,IN HINF hInf,IN LPCWSTR ServiceSection,IN LPCWSTR ServiceName,IN UINT ServiceFlags)1780 static BOOL InstallOneService(
1781     struct DeviceInfoSet *list,
1782     IN HINF hInf,
1783     IN LPCWSTR ServiceSection,
1784     IN LPCWSTR ServiceName,
1785     IN UINT ServiceFlags)
1786 {
1787     SC_HANDLE hSCManager = NULL;
1788     SC_HANDLE hService = NULL;
1789     LPDWORD GroupOrder = NULL;
1790     LPQUERY_SERVICE_CONFIGW ServiceConfig = NULL;
1791     HKEY hServicesKey, hServiceKey;
1792     LONG rc;
1793     BOOL ret = FALSE;
1794 
1795     HKEY hGroupOrderListKey = NULL;
1796     LPWSTR ServiceBinary = NULL;
1797     LPWSTR LoadOrderGroup = NULL;
1798     LPWSTR DisplayName = NULL;
1799     LPWSTR Description = NULL;
1800     LPWSTR Dependencies = NULL;
1801     LPWSTR StartName = NULL;
1802     LPWSTR SecurityDescriptor = NULL;
1803     PSECURITY_DESCRIPTOR sd = NULL;
1804     INT ServiceType, StartType, ErrorControl;
1805     DWORD dwRegType;
1806     DWORD tagId = (DWORD)-1;
1807     BOOL useTag;
1808 
1809     if (!GetIntField(hInf, ServiceSection, ServiceTypeKey, &ServiceType))
1810     {
1811         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1812         goto cleanup;
1813     }
1814     if (!GetIntField(hInf, ServiceSection, StartTypeKey, &StartType))
1815     {
1816         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1817         goto cleanup;
1818     }
1819     if (!GetIntField(hInf, ServiceSection, ErrorControlKey, &ErrorControl))
1820     {
1821         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1822         goto cleanup;
1823     }
1824     useTag = (ServiceType == SERVICE_BOOT_START || ServiceType == SERVICE_SYSTEM_START);
1825 
1826     hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE);
1827     if (hSCManager == NULL)
1828         goto cleanup;
1829 
1830     if (!GetLineText(hInf, ServiceSection, ServiceBinaryKey, &ServiceBinary))
1831     {
1832         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1833         goto cleanup;
1834     }
1835 
1836     /* Adjust binary path according to the service type */
1837     FixupServiceBinaryPath(ServiceType, &ServiceBinary);
1838 
1839     /* Don't check return value, as these fields are optional and
1840      * GetLineText initialize output parameter even on failure */
1841     GetLineText(hInf, ServiceSection, LoadOrderGroupKey, &LoadOrderGroup);
1842     GetLineText(hInf, ServiceSection, DisplayNameKey, &DisplayName);
1843     GetLineText(hInf, ServiceSection, DescriptionKey, &Description);
1844     GetLineText(hInf, ServiceSection, DependenciesKey, &Dependencies);
1845     GetLineText(hInf, ServiceSection, StartNameKey, &StartName);
1846 
1847     /* If there is no group, we must not request a tag */
1848     if (!LoadOrderGroup || !*LoadOrderGroup)
1849         useTag = FALSE;
1850 
1851     hService = OpenServiceW(
1852         hSCManager,
1853         ServiceName,
1854         DELETE | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | WRITE_DAC);
1855     if (hService == NULL && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
1856         goto cleanup;
1857 
1858     if (hService && (ServiceFlags & SPSVCINST_DELETEEVENTLOGENTRY))
1859     {
1860         ret = DeleteService(hService);
1861         if (!ret && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
1862             goto cleanup;
1863     }
1864 
1865     if (hService == NULL)
1866     {
1867         /* Create new service */
1868         hService = CreateServiceW(
1869             hSCManager,
1870             ServiceName,
1871             DisplayName,
1872             WRITE_DAC,
1873             ServiceType,
1874             StartType,
1875             ErrorControl,
1876             ServiceBinary,
1877             LoadOrderGroup,
1878             useTag ? &tagId : NULL,
1879             Dependencies,
1880             StartName,
1881             NULL);
1882         if (hService == NULL)
1883             goto cleanup;
1884     }
1885     else
1886     {
1887         DWORD bufferSize;
1888         /* Read current configuration */
1889         if (!QueryServiceConfigW(hService, NULL, 0, &bufferSize))
1890         {
1891             if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1892                 goto cleanup;
1893             ServiceConfig = MyMalloc(bufferSize);
1894             if (!ServiceConfig)
1895             {
1896                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1897                 goto cleanup;
1898             }
1899             if (!QueryServiceConfigW(hService, ServiceConfig, bufferSize, &bufferSize))
1900                 goto cleanup;
1901         }
1902         tagId = ServiceConfig->dwTagId;
1903 
1904         /* Update configuration */
1905         ret = ChangeServiceConfigW(
1906             hService,
1907             ServiceType,
1908             (ServiceFlags & SPSVCINST_NOCLOBBER_STARTTYPE) ? SERVICE_NO_CHANGE : StartType,
1909             (ServiceFlags & SPSVCINST_NOCLOBBER_ERRORCONTROL) ? SERVICE_NO_CHANGE : ErrorControl,
1910             ServiceBinary,
1911             (ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP && ServiceConfig->lpLoadOrderGroup) ? NULL : LoadOrderGroup,
1912             useTag ? &tagId : NULL,
1913             (ServiceFlags & SPSVCINST_NOCLOBBER_DEPENDENCIES && ServiceConfig->lpDependencies) ? NULL : Dependencies,
1914             StartName,
1915             NULL,
1916             (ServiceFlags & SPSVCINST_NOCLOBBER_DISPLAYNAME && ServiceConfig->lpDisplayName) ? NULL : DisplayName);
1917         if (!ret)
1918             goto cleanup;
1919     }
1920 
1921     /* Set security */
1922     if (GetLineText(hInf, ServiceSection, SecurityKey, &SecurityDescriptor))
1923     {
1924         ret = ConvertStringSecurityDescriptorToSecurityDescriptorW(SecurityDescriptor, SDDL_REVISION_1, &sd, NULL);
1925         if (!ret)
1926             goto cleanup;
1927         ret = SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, sd);
1928         if (!ret)
1929             goto cleanup;
1930     }
1931 
1932     /* FIXME: use Description and SPSVCINST_NOCLOBBER_DESCRIPTION */
1933 
1934     if (useTag)
1935     {
1936         /* Add the tag to SYSTEM\CurrentControlSet\Control\GroupOrderList key */
1937         LPCWSTR lpLoadOrderGroup;
1938         DWORD bufferSize;
1939 
1940         lpLoadOrderGroup = LoadOrderGroup;
1941         if ((ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) && ServiceConfig && ServiceConfig->lpLoadOrderGroup)
1942             lpLoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
1943 
1944         rc = RegOpenKeyW(
1945             list ? list->HKLM : HKEY_LOCAL_MACHINE,
1946             GroupOrderListKey,
1947             &hGroupOrderListKey);
1948         if (rc != ERROR_SUCCESS)
1949         {
1950             SetLastError(rc);
1951             goto cleanup;
1952         }
1953         rc = RegQueryValueExW(hGroupOrderListKey, lpLoadOrderGroup, NULL, &dwRegType, NULL, &bufferSize);
1954         if (rc == ERROR_FILE_NOT_FOUND)
1955             bufferSize = sizeof(DWORD);
1956         else if (rc != ERROR_SUCCESS)
1957         {
1958             SetLastError(rc);
1959             goto cleanup;
1960         }
1961         else if (dwRegType != REG_BINARY || bufferSize == 0 || bufferSize % sizeof(DWORD) != 0)
1962         {
1963             SetLastError(ERROR_GEN_FAILURE);
1964             goto cleanup;
1965         }
1966         /* Allocate buffer to store existing data + the new tag */
1967         GroupOrder = MyMalloc(bufferSize + sizeof(DWORD));
1968         if (!GroupOrder)
1969         {
1970             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1971             goto cleanup;
1972         }
1973         if (rc == ERROR_SUCCESS)
1974         {
1975             /* Read existing data */
1976             rc = RegQueryValueExW(
1977                 hGroupOrderListKey,
1978                 lpLoadOrderGroup,
1979                 NULL,
1980                 NULL,
1981                 (BYTE*)GroupOrder,
1982                 &bufferSize);
1983             if (rc != ERROR_SUCCESS)
1984             {
1985                 SetLastError(rc);
1986                 goto cleanup;
1987             }
1988             if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1989                 memmove(&GroupOrder[2], &GroupOrder[1], bufferSize - sizeof(DWORD));
1990         }
1991         else
1992         {
1993             GroupOrder[0] = 0;
1994         }
1995         GroupOrder[0]++;
1996         if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1997             GroupOrder[1] = tagId;
1998         else
1999             GroupOrder[bufferSize / sizeof(DWORD)] = tagId;
2000 
2001         rc = RegSetValueExW(
2002             hGroupOrderListKey,
2003             lpLoadOrderGroup,
2004             0,
2005             REG_BINARY,
2006             (BYTE*)GroupOrder,
2007             bufferSize + sizeof(DWORD));
2008         if (rc != ERROR_SUCCESS)
2009         {
2010             SetLastError(rc);
2011             goto cleanup;
2012         }
2013     }
2014 
2015     /* Handle AddReg and DelReg */
2016     rc = RegOpenKeyExW(
2017         list ? list->HKLM : HKEY_LOCAL_MACHINE,
2018         REGSTR_PATH_SERVICES,
2019         0,
2020         READ_CONTROL,
2021         &hServicesKey);
2022     if (rc != ERROR_SUCCESS)
2023     {
2024         SetLastError(rc);
2025         goto cleanup;
2026     }
2027     rc = RegOpenKeyExW(
2028         hServicesKey,
2029         ServiceName,
2030         0,
2031         KEY_READ | KEY_WRITE,
2032         &hServiceKey);
2033     RegCloseKey(hServicesKey);
2034     if (rc != ERROR_SUCCESS)
2035     {
2036         SetLastError(rc);
2037         goto cleanup;
2038     }
2039 
2040     ret = SetupInstallFromInfSectionW(
2041         NULL,
2042         hInf,
2043         ServiceSection,
2044         SPINST_REGISTRY,
2045         hServiceKey,
2046         NULL,
2047         0,
2048         NULL,
2049         NULL,
2050         NULL,
2051         NULL);
2052     RegCloseKey(hServiceKey);
2053 
2054 cleanup:
2055     if (hSCManager != NULL)
2056         CloseServiceHandle(hSCManager);
2057     if (hService != NULL)
2058         CloseServiceHandle(hService);
2059     if (hGroupOrderListKey != NULL)
2060         RegCloseKey(hGroupOrderListKey);
2061     if (sd != NULL)
2062         LocalFree(sd);
2063     MyFree(ServiceConfig);
2064     MyFree(ServiceBinary);
2065     MyFree(LoadOrderGroup);
2066     MyFree(DisplayName);
2067     MyFree(Description);
2068     MyFree(Dependencies);
2069     MyFree(SecurityDescriptor);
2070     MyFree(GroupOrder);
2071     MyFree(StartName);
2072 
2073     TRACE("Returning %d\n", ret);
2074     return ret;
2075 }
2076 
2077 
2078 /***********************************************************************
2079  *		SetupInstallServicesFromInfSectionExW  (SETUPAPI.@)
2080  */
SetupInstallServicesFromInfSectionExW(HINF hinf,PCWSTR sectionname,DWORD flags,HDEVINFO DeviceInfoSet,PSP_DEVINFO_DATA DeviceInfoData,PVOID reserved1,PVOID reserved2)2081 BOOL WINAPI SetupInstallServicesFromInfSectionExW( HINF hinf, PCWSTR sectionname, DWORD flags, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PVOID reserved1, PVOID reserved2 )
2082 {
2083     struct DeviceInfoSet *list = NULL;
2084     BOOL ret = FALSE;
2085 
2086     TRACE("%p, %s, 0x%lx, %p, %p, %p, %p\n", hinf, debugstr_w(sectionname),
2087         flags, DeviceInfoSet, DeviceInfoData, reserved1, reserved2);
2088 
2089     if (!sectionname)
2090         SetLastError(ERROR_INVALID_PARAMETER);
2091     else if (flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE))
2092     {
2093         TRACE("Unknown flags: 0x%08lx\n", flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE));
2094         SetLastError(ERROR_INVALID_FLAGS);
2095     }
2096     else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
2097         SetLastError(ERROR_INVALID_HANDLE);
2098     else if (DeviceInfoSet && (list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
2099         SetLastError(ERROR_INVALID_HANDLE);
2100     else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
2101         SetLastError(ERROR_INVALID_USER_BUFFER);
2102     else if (reserved1 != NULL || reserved2 != NULL)
2103         SetLastError(ERROR_INVALID_PARAMETER);
2104     else
2105     {
2106         struct needs_callback_info needs_info;
2107         LPWSTR ServiceName = NULL;
2108         LPWSTR ServiceSection = NULL;
2109         INT ServiceFlags;
2110         INFCONTEXT ContextService;
2111         BOOL bNeedReboot = FALSE;
2112 
2113         /* Parse 'Include' and 'Needs' directives */
2114         iterate_section_fields( hinf, sectionname, Include, include_callback, NULL);
2115         needs_info.type = 1;
2116         needs_info.flags = flags;
2117         needs_info.devinfo = DeviceInfoSet;
2118         needs_info.devinfo_data = DeviceInfoData;
2119         needs_info.reserved1 = reserved1;
2120         needs_info.reserved2 = reserved2;
2121         iterate_section_fields( hinf, sectionname, Needs, needs_callback, &needs_info);
2122 
2123         if (flags & SPSVCINST_STOPSERVICE)
2124         {
2125             FIXME("Stopping the device not implemented\n");
2126             /* This may lead to require a reboot */
2127             /* bNeedReboot = TRUE; */
2128 #if 0
2129             SERVICE_STATUS ServiceStatus;
2130             ret = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
2131             if (!ret && GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
2132                 goto done;
2133             if (ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING && ServiceStatus.dwCurrentState != SERVICE_STOPPED)
2134             {
2135                 SetLastError(ERROR_INSTALL_SERVICE_FAILURE);
2136                 goto done;
2137             }
2138 #endif
2139             flags &= ~SPSVCINST_STOPSERVICE;
2140         }
2141 
2142         if (!(ret = SetupFindFirstLineW( hinf, sectionname, NULL, &ContextService )))
2143         {
2144             SetLastError( ERROR_SECTION_NOT_FOUND );
2145             goto done;
2146         }
2147 
2148         ret = SetupFindFirstLineW(hinf, sectionname, AddService, &ContextService);
2149         while (ret)
2150         {
2151             if (!GetStringField(&ContextService, 1, &ServiceName))
2152                 goto done;
2153 
2154             ret = SetupGetIntField(
2155                 &ContextService,
2156                 2, /* Field index */
2157                 &ServiceFlags);
2158             if (!ret)
2159             {
2160                 /* The field may be empty. Ignore the error */
2161                 ServiceFlags = 0;
2162             }
2163 
2164             if (!GetStringField(&ContextService, 3, &ServiceSection))
2165                 goto done;
2166 
2167             ret = InstallOneService(list, hinf, ServiceSection, ServiceName, (ServiceFlags & ~SPSVCINST_ASSOCSERVICE) | flags);
2168             if (!ret)
2169                 goto done;
2170 
2171             if (ServiceFlags & SPSVCINST_ASSOCSERVICE)
2172             {
2173                 ret = SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, (LPBYTE)ServiceName, (strlenW(ServiceName) + 1) * sizeof(WCHAR));
2174                 if (!ret)
2175                     goto done;
2176             }
2177 
2178             HeapFree(GetProcessHeap(), 0, ServiceName);
2179             HeapFree(GetProcessHeap(), 0, ServiceSection);
2180             ServiceName = ServiceSection = NULL;
2181             ret = SetupFindNextMatchLineW(&ContextService, AddService, &ContextService);
2182         }
2183 
2184         if (bNeedReboot)
2185             SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
2186         else
2187             SetLastError(ERROR_SUCCESS);
2188         ret = TRUE;
2189     }
2190 done:
2191     TRACE("Returning %d\n", ret);
2192     return ret;
2193 }
2194 
2195 
2196 /***********************************************************************
2197  *		SetupCopyOEMInfA  (SETUPAPI.@)
2198  */
SetupCopyOEMInfA(IN PCSTR SourceInfFileName,IN PCSTR OEMSourceMediaLocation,IN DWORD OEMSourceMediaType,IN DWORD CopyStyle,OUT PSTR DestinationInfFileName OPTIONAL,IN DWORD DestinationInfFileNameSize,OUT PDWORD RequiredSize OPTIONAL,OUT PSTR * DestinationInfFileNameComponent OPTIONAL)2199 BOOL WINAPI SetupCopyOEMInfA(
2200         IN PCSTR SourceInfFileName,
2201         IN PCSTR OEMSourceMediaLocation,
2202         IN DWORD OEMSourceMediaType,
2203         IN DWORD CopyStyle,
2204         OUT PSTR DestinationInfFileName OPTIONAL,
2205         IN DWORD DestinationInfFileNameSize,
2206         OUT PDWORD RequiredSize OPTIONAL,
2207         OUT PSTR* DestinationInfFileNameComponent OPTIONAL)
2208 {
2209     PWSTR SourceInfFileNameW = NULL;
2210     PWSTR OEMSourceMediaLocationW = NULL;
2211     PWSTR DestinationInfFileNameW = NULL;
2212     PWSTR DestinationInfFileNameComponentW = NULL;
2213     BOOL ret = FALSE;
2214     DWORD size;
2215 
2216     TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2217         SourceInfFileName, OEMSourceMediaLocation, OEMSourceMediaType,
2218         CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2219         RequiredSize, DestinationInfFileNameComponent);
2220 
2221     if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2222         SetLastError(ERROR_INVALID_PARAMETER);
2223     else if (!(SourceInfFileNameW = pSetupMultiByteToUnicode(SourceInfFileName, CP_ACP)))
2224         SetLastError(ERROR_INVALID_PARAMETER);
2225     else if (OEMSourceMediaType != SPOST_NONE && !(OEMSourceMediaLocationW = pSetupMultiByteToUnicode(OEMSourceMediaLocation, CP_ACP)))
2226         SetLastError(ERROR_INVALID_PARAMETER);
2227     else
2228     {
2229         if (DestinationInfFileNameSize != 0)
2230         {
2231             DestinationInfFileNameW = MyMalloc(DestinationInfFileNameSize * sizeof(WCHAR));
2232             if (!DestinationInfFileNameW)
2233             {
2234                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2235                 goto cleanup;
2236             }
2237         }
2238 
2239         ret = SetupCopyOEMInfW(
2240             SourceInfFileNameW,
2241             OEMSourceMediaLocationW,
2242             OEMSourceMediaType,
2243             CopyStyle,
2244             DestinationInfFileNameW,
2245             DestinationInfFileNameSize,
2246             &size,
2247             DestinationInfFileNameComponent ? &DestinationInfFileNameComponentW : NULL);
2248         if (!ret)
2249         {
2250             if (RequiredSize) *RequiredSize = size;
2251             goto cleanup;
2252         }
2253 
2254         if (DestinationInfFileNameSize != 0)
2255         {
2256             if (WideCharToMultiByte(CP_ACP, 0, DestinationInfFileNameW, -1,
2257                 DestinationInfFileName, DestinationInfFileNameSize, NULL, NULL) == 0)
2258             {
2259                 DestinationInfFileName[0] = '\0';
2260                 goto cleanup;
2261             }
2262         }
2263         if (DestinationInfFileNameComponent)
2264         {
2265             if (DestinationInfFileNameComponentW)
2266                 *DestinationInfFileNameComponent = &DestinationInfFileName[DestinationInfFileNameComponentW - DestinationInfFileNameW];
2267             else
2268                 *DestinationInfFileNameComponent = NULL;
2269         }
2270         ret = TRUE;
2271     }
2272 
2273 cleanup:
2274     MyFree(SourceInfFileNameW);
2275     MyFree(OEMSourceMediaLocationW);
2276     MyFree(DestinationInfFileNameW);
2277     TRACE("Returning %d\n", ret);
2278     if (ret) SetLastError(ERROR_SUCCESS);
2279     return ret;
2280 }
2281 
compare_files(HANDLE file1,HANDLE file2)2282 static int compare_files( HANDLE file1, HANDLE file2 )
2283 {
2284     char buffer1[2048];
2285     char buffer2[2048];
2286     DWORD size1;
2287     DWORD size2;
2288 
2289     while( ReadFile(file1, buffer1, sizeof(buffer1), &size1, NULL) &&
2290            ReadFile(file2, buffer2, sizeof(buffer2), &size2, NULL) )
2291     {
2292         int ret;
2293         if (size1 != size2)
2294             return size1 > size2 ? 1 : -1;
2295         if (!size1)
2296             return 0;
2297         ret = memcmp( buffer1, buffer2, size1 );
2298         if (ret)
2299             return ret;
2300     }
2301 
2302     return 0;
2303 }
2304 
2305 /***********************************************************************
2306  *		SetupCopyOEMInfW  (SETUPAPI.@)
2307  */
SetupCopyOEMInfW(IN PCWSTR SourceInfFileName,IN PCWSTR OEMSourceMediaLocation,IN DWORD OEMSourceMediaType,IN DWORD CopyStyle,OUT PWSTR DestinationInfFileName OPTIONAL,IN DWORD DestinationInfFileNameSize,OUT PDWORD RequiredSize OPTIONAL,OUT PWSTR * DestinationInfFileNameComponent OPTIONAL)2308 BOOL WINAPI SetupCopyOEMInfW(
2309         IN PCWSTR SourceInfFileName,
2310         IN PCWSTR OEMSourceMediaLocation,
2311         IN DWORD OEMSourceMediaType,
2312         IN DWORD CopyStyle,
2313         OUT PWSTR DestinationInfFileName OPTIONAL,
2314         IN DWORD DestinationInfFileNameSize,
2315         OUT PDWORD RequiredSize OPTIONAL,
2316         OUT PWSTR* DestinationInfFileNameComponent OPTIONAL)
2317 {
2318     BOOL ret = FALSE;
2319 
2320     TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2321         debugstr_w(SourceInfFileName), debugstr_w(OEMSourceMediaLocation), OEMSourceMediaType,
2322         CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2323         RequiredSize, DestinationInfFileNameComponent);
2324 
2325     if (!SourceInfFileName)
2326         SetLastError(ERROR_INVALID_PARAMETER);
2327     else if (OEMSourceMediaType != SPOST_NONE && OEMSourceMediaType != SPOST_PATH && OEMSourceMediaType != SPOST_URL)
2328         SetLastError(ERROR_INVALID_PARAMETER);
2329     else if (CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY))
2330     {
2331         TRACE("Unknown flags: 0x%08lx\n", CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY));
2332         SetLastError(ERROR_INVALID_FLAGS);
2333     }
2334     else if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2335         SetLastError(ERROR_INVALID_PARAMETER);
2336     else if (CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY)
2337     {
2338         FIXME("CopyStyle 0x%x not supported\n", SP_COPY_OEMINF_CATALOG_ONLY);
2339         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2340     }
2341     else
2342     {
2343         HANDLE hSearch = INVALID_HANDLE_VALUE;
2344         WIN32_FIND_DATAW FindFileData;
2345         BOOL AlreadyExists;
2346         DWORD NextFreeNumber = 0;
2347         SIZE_T len;
2348         LPWSTR pFullFileName = NULL;
2349         LPWSTR pFileName; /* Pointer into pFullFileName buffer */
2350         HANDLE hSourceFile = INVALID_HANDLE_VALUE;
2351 
2352         if (OEMSourceMediaType == SPOST_PATH || OEMSourceMediaType == SPOST_URL)
2353             FIXME("OEMSourceMediaType 0x%lx ignored\n", OEMSourceMediaType);
2354 
2355         /* Check if source file exists, and open it */
2356         if (strchrW(SourceInfFileName, '\\' ) || strchrW(SourceInfFileName, '/' ))
2357         {
2358             WCHAR *path;
2359 
2360             if (!(len = GetFullPathNameW(SourceInfFileName, 0, NULL, NULL)))
2361                 return FALSE;
2362             if (!(path = MyMalloc(len * sizeof(WCHAR))))
2363             {
2364                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2365                 return FALSE;
2366             }
2367             GetFullPathNameW(SourceInfFileName, len, path, NULL);
2368             hSourceFile = CreateFileW(
2369                 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2370                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2371                 NULL, OPEN_EXISTING, 0, NULL);
2372             MyFree(path);
2373         }
2374         else  /* try Windows directory */
2375         {
2376             WCHAR *path, *p;
2377             static const WCHAR Inf[]      = {'\\','i','n','f','\\',0};
2378             static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
2379 
2380             len = GetWindowsDirectoryW(NULL, 0) + strlenW(SourceInfFileName) + 12;
2381             if (!(path = MyMalloc(len * sizeof(WCHAR))))
2382             {
2383                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2384                 return FALSE;
2385             }
2386             GetWindowsDirectoryW(path, len);
2387             p = path + strlenW(path);
2388             strcpyW(p, Inf);
2389             strcatW(p, SourceInfFileName);
2390             hSourceFile = CreateFileW(
2391                 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2392                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2393                 NULL, OPEN_EXISTING, 0, NULL);
2394             if (hSourceFile == INVALID_HANDLE_VALUE)
2395             {
2396                 strcpyW(p, System32);
2397                 strcatW(p, SourceInfFileName);
2398                 hSourceFile = CreateFileW(
2399                     path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2400                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2401                     NULL, OPEN_EXISTING, 0, NULL);
2402             }
2403             MyFree(path);
2404         }
2405         if (hSourceFile == INVALID_HANDLE_VALUE)
2406         {
2407             SetLastError(ERROR_FILE_NOT_FOUND);
2408             goto cleanup;
2409         }
2410 
2411         /* Prepare .inf file specification */
2412         len = MAX_PATH + 1 + strlenW(InfDirectory) + 13;
2413         pFullFileName = MyMalloc(len * sizeof(WCHAR));
2414         if (!pFullFileName)
2415         {
2416             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2417             goto cleanup;
2418         }
2419         len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
2420         if (len == 0 || len > MAX_PATH)
2421             goto cleanup;
2422         if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2423             strcatW(pFullFileName, BackSlash);
2424         strcatW(pFullFileName, InfDirectory);
2425         pFileName = &pFullFileName[strlenW(pFullFileName)];
2426 
2427         /* Search if the specified .inf file already exists in %WINDIR%\Inf */
2428         AlreadyExists = FALSE;
2429         strcpyW(pFileName, OemFileMask);
2430         hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2431         if (hSearch != INVALID_HANDLE_VALUE)
2432         {
2433             LARGE_INTEGER SourceFileSize;
2434 
2435             if (GetFileSizeEx(hSourceFile, &SourceFileSize))
2436             {
2437                 do
2438                 {
2439                     LARGE_INTEGER DestFileSize;
2440                     HANDLE hDestFile;
2441 
2442                     strcpyW(pFileName, FindFileData.cFileName);
2443                     hDestFile = CreateFileW(
2444                         pFullFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2445                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2446                         NULL, OPEN_EXISTING, 0, NULL);
2447                     if (hDestFile != INVALID_HANDLE_VALUE)
2448                     {
2449                         if (GetFileSizeEx(hDestFile, &DestFileSize)
2450                          && DestFileSize.QuadPart == SourceFileSize.QuadPart
2451                          && !compare_files(hSourceFile, hDestFile))
2452                         {
2453                             TRACE("%s already exists as %s\n",
2454                                 debugstr_w(SourceInfFileName), debugstr_w(pFileName));
2455                             AlreadyExists = TRUE;
2456                         }
2457                     }
2458                 } while (!AlreadyExists && FindNextFileW(hSearch, &FindFileData));
2459             }
2460             FindClose(hSearch);
2461             hSearch = INVALID_HANDLE_VALUE;
2462         }
2463 
2464         if (!AlreadyExists && CopyStyle & SP_COPY_REPLACEONLY)
2465         {
2466             /* FIXME: set DestinationInfFileName, RequiredSize, DestinationInfFileNameComponent */
2467             SetLastError(ERROR_FILE_NOT_FOUND);
2468             goto cleanup;
2469         }
2470         else if (AlreadyExists && (CopyStyle & SP_COPY_NOOVERWRITE))
2471         {
2472             DWORD Size = strlenW(pFileName) + 1;
2473 
2474             if (RequiredSize)
2475                 *RequiredSize = Size;
2476             if (DestinationInfFileNameSize == 0)
2477                 SetLastError(ERROR_FILE_EXISTS);
2478             else if (DestinationInfFileNameSize < Size)
2479                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2480             else
2481             {
2482                 SetLastError(ERROR_FILE_EXISTS);
2483                 strcpyW(DestinationInfFileName, pFileName);
2484             }
2485             goto cleanup;
2486         }
2487 
2488         /* Search the number to give to OEM??.INF */
2489         strcpyW(pFileName, OemFileMask);
2490         hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2491         if (hSearch == INVALID_HANDLE_VALUE)
2492         {
2493             if (GetLastError() != ERROR_FILE_NOT_FOUND)
2494                 goto cleanup;
2495         }
2496         else
2497         {
2498             do
2499             {
2500                 DWORD CurrentNumber;
2501                 if (swscanf(FindFileData.cFileName, OemFileSpecification, &CurrentNumber) == 1
2502                     && CurrentNumber <= 99999)
2503                 {
2504                     if (CurrentNumber >= NextFreeNumber)
2505                         NextFreeNumber = CurrentNumber + 1;
2506                 }
2507             } while (FindNextFileW(hSearch, &FindFileData));
2508         }
2509 
2510         if (NextFreeNumber > 99999)
2511         {
2512             ERR("Too much custom .inf files\n");
2513             SetLastError(ERROR_GEN_FAILURE);
2514             goto cleanup;
2515         }
2516 
2517         /* Create the full path: %WINDIR%\Inf\OEM{XXXXX}.inf */
2518         sprintfW(pFileName, OemFileSpecification, NextFreeNumber);
2519         TRACE("Next available file is %s\n", debugstr_w(pFileName));
2520 
2521         if (!CopyFileW(SourceInfFileName, pFullFileName, TRUE))
2522         {
2523             TRACE("CopyFileW() failed with error 0x%lx\n", GetLastError());
2524             goto cleanup;
2525         }
2526 
2527         len = strlenW(pFullFileName) + 1;
2528         if (RequiredSize)
2529             *RequiredSize = len;
2530         if (DestinationInfFileName)
2531         {
2532             if (DestinationInfFileNameSize >= len)
2533             {
2534                 strcpyW(DestinationInfFileName, pFullFileName);
2535                 if (DestinationInfFileNameComponent)
2536                     *DestinationInfFileNameComponent = &DestinationInfFileName[pFileName - pFullFileName];
2537             }
2538             else
2539             {
2540                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2541                 goto cleanup;
2542             }
2543         }
2544 
2545         if (CopyStyle & SP_COPY_DELETESOURCE)
2546         {
2547             if (!DeleteFileW(SourceInfFileName))
2548             {
2549                 TRACE("DeleteFileW() failed with error 0x%lx\n", GetLastError());
2550                 goto cleanup;
2551             }
2552         }
2553 
2554         ret = TRUE;
2555 
2556 cleanup:
2557         if (hSourceFile != INVALID_HANDLE_VALUE)
2558             CloseHandle(hSourceFile);
2559         if (hSearch != INVALID_HANDLE_VALUE)
2560             FindClose(hSearch);
2561         MyFree(pFullFileName);
2562     }
2563 
2564     TRACE("Returning %d\n", ret);
2565     if (ret) SetLastError(ERROR_SUCCESS);
2566     return ret;
2567 }
2568