advapi32: Do not terminate a regular program if it calls StartServiceCtrlDispatcher.
[wine] / programs / msiexec / msiexec.c
1 /*
2  * msiexec.exe implementation
3  *
4  * Copyright 2004 Vincent BĂ©ron
5  * Copyright 2005 Mike McCormack
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 #define WIN32_LEAN_AND_MEAN
23
24 #include <windows.h>
25 #include <msi.h>
26 #include <winsvc.h>
27 #include <objbase.h>
28 #include <stdio.h>
29
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
34
35 typedef HRESULT (WINAPI *DLLREGISTERSERVER)(void);
36 typedef HRESULT (WINAPI *DLLUNREGISTERSERVER)(void);
37
38 DWORD DoService(void);
39
40 struct string_list
41 {
42         struct string_list *next;
43         WCHAR str[1];
44 };
45
46 static const WCHAR ActionAdmin[] = {
47    'A','C','T','I','O','N','=','A','D','M','I','N',0 };
48 static const WCHAR RemoveAll[] = {
49    'R','E','M','O','V','E','=','A','L','L',0 };
50
51 static const WCHAR InstallRunOnce[] = {
52    'S','o','f','t','w','a','r','e','\\',
53    'M','i','c','r','o','s','o','f','t','\\',
54    'W','i','n','d','o','w','s','\\',
55    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
56    'I','n','s','t','a','l','l','e','r','\\',
57    'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
58
59 static void ShowUsage(int ExitCode)
60 {
61     WCHAR msiexec_version[40];
62     WCHAR filename[MAX_PATH];
63     LPWSTR msi_res;
64     LPWSTR msiexec_help;
65     HMODULE hmsi = GetModuleHandleA("msi.dll");
66     DWORD len;
67     DWORD res;
68
69     /* MsiGetFileVersion need the full path */
70     *filename = 0;
71     res = GetModuleFileNameW(hmsi, filename, sizeof(filename) / sizeof(filename[0]));
72     if (!res)
73         WINE_ERR("GetModuleFileName failed: %d\n", GetLastError());
74
75     len = sizeof(msiexec_version) / sizeof(msiexec_version[0]);
76     *msiexec_version = 0;
77     res = MsiGetFileVersionW(filename, msiexec_version, &len, NULL, NULL);
78     if (res)
79         WINE_ERR("MsiGetFileVersion failed with %d\n", res);
80
81     /* Return the length of the resource.
82        No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
83     len = LoadStringW(hmsi, 10, (LPWSTR) &msi_res, 0);
84
85     msi_res = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
86     msiexec_help = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) + sizeof(msiexec_version));
87     if (msi_res && msiexec_help) {
88         *msi_res = 0;
89         LoadStringW(hmsi, 10, msi_res, len + 1);
90
91         sprintfW(msiexec_help, msi_res, msiexec_version);
92         MsiMessageBoxW(0, msiexec_help, NULL, 0, GetUserDefaultLangID(), 0);
93     }
94     HeapFree(GetProcessHeap(), 0, msi_res);
95     HeapFree(GetProcessHeap(), 0, msiexec_help);
96     ExitProcess(ExitCode);
97 }
98
99 static BOOL IsProductCode(LPWSTR str)
100 {
101         GUID ProductCode;
102
103         if(lstrlenW(str) != 38)
104                 return FALSE;
105         return ( (CLSIDFromString(str, &ProductCode) == NOERROR) );
106
107 }
108
109 static VOID StringListAppend(struct string_list **list, LPCWSTR str)
110 {
111         struct string_list *entry;
112
113         entry = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(struct string_list, str[lstrlenW(str) + 1]));
114         if(!entry)
115         {
116                 WINE_ERR("Out of memory!\n");
117                 ExitProcess(1);
118         }
119         lstrcpyW(entry->str, str);
120         entry->next = NULL;
121
122         /*
123          * Ignoring o(n^2) time complexity to add n strings for simplicity,
124          *  add the string to the end of the list to preserve the order.
125          */
126         while( *list )
127                 list = &(*list)->next;
128         *list = entry;
129 }
130
131 static LPWSTR build_properties(struct string_list *property_list)
132 {
133         struct string_list *list;
134         LPWSTR ret, p, value;
135         DWORD len;
136         BOOL needs_quote;
137
138         if(!property_list)
139                 return NULL;
140
141         /* count the space we need */
142         len = 1;
143         for(list = property_list; list; list = list->next)
144                 len += lstrlenW(list->str) + 3;
145
146         ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
147
148         /* add a space before each string, and quote the value */
149         p = ret;
150         for(list = property_list; list; list = list->next)
151         {
152                 value = strchrW(list->str,'=');
153                 if(!value)
154                         continue;
155                 len = value - list->str;
156                 *p++ = ' ';
157                 memcpy(p, list->str, len * sizeof(WCHAR));
158                 p += len;
159                 *p++ = '=';
160
161                 /* check if the value contains spaces and maybe quote it */
162                 value++;
163                 needs_quote = strchrW(value,' ') ? 1 : 0;
164                 if(needs_quote)
165                         *p++ = '"';
166                 len = lstrlenW(value);
167                 memcpy(p, value, len * sizeof(WCHAR));
168                 p += len;
169                 if(needs_quote)
170                         *p++ = '"';
171         }
172         *p = 0;
173
174         WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );
175
176         return ret;
177 }
178
179 static LPWSTR build_transforms(struct string_list *transform_list)
180 {
181         struct string_list *list;
182         LPWSTR ret, p;
183         DWORD len;
184
185         /* count the space we need */
186         len = 1;
187         for(list = transform_list; list; list = list->next)
188                 len += lstrlenW(list->str) + 1;
189
190         ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
191
192         /* add all the transforms with a semicolon between each one */
193         p = ret;
194         for(list = transform_list; list; list = list->next)
195         {
196                 len = lstrlenW(list->str);
197                 lstrcpynW(p, list->str, len );
198                 p += len;
199                 if(list->next)
200                         *p++ = ';';
201         }
202         *p = 0;
203
204         return ret;
205 }
206
207 static DWORD msi_atou(LPCWSTR str)
208 {
209         DWORD ret = 0;
210         while(*str >= '0' && *str <= '9')
211         {
212                 ret *= 10;
213                 ret += (*str - '0');
214                 str++;
215         }
216         return ret;
217 }
218
219 static LPWSTR msi_strdup(LPCWSTR str)
220 {
221         DWORD len = lstrlenW(str)+1;
222         LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
223         lstrcpyW(ret, str);
224         return ret;
225 }
226
227 /* str1 is the same as str2, ignoring case */
228 static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
229 {
230         DWORD len, ret;
231         LPWSTR strW;
232
233         len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
234         if( !len )
235                 return FALSE;
236         if( lstrlenW(str1) != (len-1) )
237                 return FALSE;
238         strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
239         MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
240         ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len);
241         HeapFree(GetProcessHeap(), 0, strW);
242         return (ret == CSTR_EQUAL);
243 }
244
245 /* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
246 static BOOL msi_option_equal(LPCWSTR str1, LPCSTR str2)
247 {
248     if (str1[0] != '/' && str1[0] != '-')
249         return FALSE;
250
251     /* skip over the hyphen or slash */
252     return msi_strequal(str1 + 1, str2);
253 }
254
255 /* str2 is at the beginning of str1, ignoring case */
256 static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2)
257 {
258         DWORD len, ret;
259         LPWSTR strW;
260
261         len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
262         if( !len )
263                 return FALSE;
264         if( lstrlenW(str1) < (len-1) )
265                 return FALSE;
266         strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
267         MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
268         ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1);
269         HeapFree(GetProcessHeap(), 0, strW);
270         return (ret == CSTR_EQUAL);
271 }
272
273 /* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
274 static BOOL msi_option_prefix(LPCWSTR str1, LPCSTR str2)
275 {
276     if (str1[0] != '/' && str1[0] != '-')
277         return FALSE;
278
279     /* skip over the hyphen or slash */
280     return msi_strprefix(str1 + 1, str2);
281 }
282
283 static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
284 {
285         VOID* (*proc)(void);
286
287         *DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
288         if(!*DllHandle)
289         {
290                 fprintf(stderr, "Unable to load dll %s\n", wine_dbgstr_w(DllName));
291                 ExitProcess(1);
292         }
293         proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
294         if(!proc)
295         {
296                 fprintf(stderr, "Dll %s does not implement function %s\n",
297                         wine_dbgstr_w(DllName), ProcName);
298                 FreeLibrary(*DllHandle);
299                 ExitProcess(1);
300         }
301
302         return proc;
303 }
304
305 static DWORD DoDllRegisterServer(LPCWSTR DllName)
306 {
307         HRESULT hr;
308         DLLREGISTERSERVER pfDllRegisterServer = NULL;
309         HMODULE DllHandle = NULL;
310
311         pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
312
313         hr = pfDllRegisterServer();
314         if(FAILED(hr))
315         {
316                 fprintf(stderr, "Failed to register dll %s\n", wine_dbgstr_w(DllName));
317                 return 1;
318         }
319         printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
320         if(DllHandle)
321                 FreeLibrary(DllHandle);
322         return 0;
323 }
324
325 static DWORD DoDllUnregisterServer(LPCWSTR DllName)
326 {
327         HRESULT hr;
328         DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
329         HMODULE DllHandle = NULL;
330
331         pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
332
333         hr = pfDllUnregisterServer();
334         if(FAILED(hr))
335         {
336                 fprintf(stderr, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
337                 return 1;
338         }
339         printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
340         if(DllHandle)
341                 FreeLibrary(DllHandle);
342         return 0;
343 }
344
345 static DWORD DoRegServer(void)
346 {
347     static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
348     static const WCHAR msiexecW[] = {'\\','m','s','i','e','x','e','c',' ','/','V',0};
349     SC_HANDLE scm, service;
350     WCHAR path[MAX_PATH+12];
351     DWORD len, ret = 0;
352
353     if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CREATE_SERVICE)))
354     {
355         fprintf(stderr, "Failed to open the service control manager.\n");
356         return 1;
357     }
358     len = GetSystemDirectoryW(path, MAX_PATH);
359     lstrcpyW(path + len, msiexecW);
360     if ((service = CreateServiceW(scm, msiserverW, msiserverW, GENERIC_ALL,
361                                   SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START,
362                                   SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, NULL, NULL)))
363     {
364         CloseServiceHandle(service);
365     }
366     else if (GetLastError() != ERROR_SERVICE_EXISTS)
367     {
368         fprintf(stderr, "Failed to create MSI service\n");
369         ret = 1;
370     }
371     CloseServiceHandle(scm);
372     return ret;
373 }
374
375 static DWORD DoUnregServer(void)
376 {
377     static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
378     SC_HANDLE scm, service;
379     DWORD ret = 0;
380
381     if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CONNECT)))
382     {
383         fprintf(stderr, "Failed to open service control manager\n");
384         return 1;
385     }
386     if ((service = OpenServiceW(scm, msiserverW, DELETE)))
387     {
388         if (!DeleteService(service))
389         {
390             fprintf(stderr, "Failed to delete MSI service\n");
391             ret = 1;
392         }
393         CloseServiceHandle(service);
394     }
395     else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
396     {
397         fprintf(stderr, "Failed to open MSI service\n");
398         ret = 1;
399     }
400     CloseServiceHandle(scm);
401     return ret;
402 }
403
404 static INT DoEmbedding( LPWSTR key )
405 {
406         printf("Remote custom actions are not supported yet\n");
407         return 1;
408 }
409
410 /*
411  * state machine to break up the command line properly
412  */
413
414 enum chomp_state
415 {
416         cs_whitespace,
417         cs_token,
418         cs_quote
419 };
420
421 static int chomp( WCHAR *str )
422 {
423         enum chomp_state state = cs_token;
424         WCHAR *p, *out;
425         int count = 1, ignore;
426
427         for( p = str, out = str; *p; p++ )
428         {
429                 ignore = 1;
430                 switch( state )
431                 {
432                 case cs_whitespace:
433                         switch( *p )
434                         {
435                         case ' ':
436                                 break;
437                         case '"':
438                                 state = cs_quote;
439                                 count++;
440                                 break;
441                         default:
442                                 count++;
443                                 ignore = 0;
444                                 state = cs_token;
445                         }
446                         break;
447
448                 case cs_token:
449                         switch( *p )
450                         {
451                         case '"':
452                                 state = cs_quote;
453                                 break;
454                         case ' ':
455                                 state = cs_whitespace;
456                                 *out++ = 0;
457                                 break;
458                         default:
459                                 ignore = 0;
460                         }
461                         break;
462
463                 case cs_quote:
464                         switch( *p )
465                         {
466                         case '"':
467                                 state = cs_token;
468                                 break;
469                         default:
470                                 ignore = 0;
471                         }
472                         break;
473                 }
474                 if( !ignore )
475                         *out++ = *p;
476         }
477
478         *out = 0;
479
480         return count;
481 }
482
483 static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
484 {
485         WCHAR **argv, *p = msi_strdup(cmdline);
486         int i, n;
487
488         n = chomp( p );
489         argv = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR*)*(n+1));
490         for( i=0; i<n; i++ )
491         {
492                 argv[i] = p;
493                 p += lstrlenW(p) + 1;
494         }
495         argv[i] = NULL;
496
497         *pargc = n;
498         *pargv = argv;
499 }
500
501 static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***pargv )
502 {
503         LONG r;
504         HKEY hkey;
505         DWORD sz = 0, type = 0;
506         WCHAR *buf;
507         BOOL ret = FALSE;
508
509         r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
510         if(r != ERROR_SUCCESS)
511                 return FALSE;
512         r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
513         if(r == ERROR_SUCCESS && type == REG_SZ)
514         {
515                 int len = lstrlenW( *pargv[0] );
516                 if (!(buf = HeapAlloc( GetProcessHeap(), 0, sz + (len + 1) * sizeof(WCHAR) )))
517                 {
518                         RegCloseKey( hkey );
519                         return FALSE;
520                 }
521                 memcpy( buf, *pargv[0], len * sizeof(WCHAR) );
522                 buf[len++] = ' ';
523                 r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)(buf + len), &sz);
524                 if( r == ERROR_SUCCESS )
525                 {
526                         process_args(buf, pargc, pargv);
527                         ret = TRUE;
528                 }
529                 HeapFree(GetProcessHeap(), 0, buf);
530         }
531         RegCloseKey(hkey);
532         return ret;
533 }
534
535 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
536 {
537         int i;
538         BOOL FunctionInstall = FALSE;
539         BOOL FunctionInstallAdmin = FALSE;
540         BOOL FunctionRepair = FALSE;
541         BOOL FunctionAdvertise = FALSE;
542         BOOL FunctionPatch = FALSE;
543         BOOL FunctionDllRegisterServer = FALSE;
544         BOOL FunctionDllUnregisterServer = FALSE;
545         BOOL FunctionRegServer = FALSE;
546         BOOL FunctionUnregServer = FALSE;
547         BOOL FunctionServer = FALSE;
548         BOOL FunctionUnknown = FALSE;
549
550         LPWSTR PackageName = NULL;
551         LPWSTR Properties = NULL;
552         struct string_list *property_list = NULL;
553
554         DWORD RepairMode = 0;
555
556         DWORD_PTR AdvertiseMode = 0;
557         struct string_list *transform_list = NULL;
558         LANGID Language = 0;
559
560         DWORD LogMode = 0;
561         LPWSTR LogFileName = NULL;
562         DWORD LogAttributes = 0;
563
564         LPWSTR PatchFileName = NULL;
565         INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
566
567         INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
568
569         LPWSTR DllName = NULL;
570         DWORD ReturnCode;
571         int argc;
572         LPWSTR *argvW = NULL;
573
574         /* parse the command line */
575         process_args( GetCommandLineW(), &argc, &argvW );
576
577         /*
578          * If the args begin with /@ IDENT then we need to load the real
579          * command line out of the RunOnceEntries key in the registry.
580          *  We do that before starting to process the real commandline,
581          * then overwrite the commandline again.
582          */
583         if(argc>1 && msi_option_equal(argvW[1], "@"))
584         {
585                 if(!process_args_from_reg( argvW[2], &argc, &argvW ))
586                         return 1;
587         }
588
589         if (argc == 3 && msi_option_equal(argvW[1], "Embedding"))
590                 return DoEmbedding( argvW[2] );
591
592         for(i = 1; i < argc; i++)
593         {
594                 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
595
596                 if (msi_option_equal(argvW[i], "regserver"))
597                 {
598                         FunctionRegServer = TRUE;
599                 }
600                 else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")
601                         ||  msi_option_equal(argvW[i], "unreg"))
602                 {
603                         FunctionUnregServer = TRUE;
604                 }
605                 else if(msi_option_prefix(argvW[i], "i") || msi_option_prefix(argvW[i], "package"))
606                 {
607                         LPWSTR argvWi = argvW[i];
608                         int argLen = (msi_option_prefix(argvW[i], "i") ? 2 : 8);
609                         FunctionInstall = TRUE;
610                         if(lstrlenW(argvW[i]) > argLen)
611                                 argvWi += argLen;
612                         else
613                         {
614                                 i++;
615                                 if(i >= argc)
616                                         ShowUsage(1);
617                                 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
618                                 argvWi = argvW[i];
619                         }
620                         PackageName = argvWi;
621                 }
622                 else if(msi_option_equal(argvW[i], "a"))
623                 {
624                         FunctionInstall = TRUE;
625                         FunctionInstallAdmin = TRUE;
626                         InstallType = INSTALLTYPE_NETWORK_IMAGE;
627                         i++;
628                         if(i >= argc)
629                                 ShowUsage(1);
630                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
631                         PackageName = argvW[i];
632                         StringListAppend(&property_list, ActionAdmin);
633                 }
634                 else if(msi_option_prefix(argvW[i], "f"))
635                 {
636                         int j;
637                         int len = lstrlenW(argvW[i]);
638                         FunctionRepair = TRUE;
639                         for(j = 2; j < len; j++)
640                         {
641                                 switch(argvW[i][j])
642                                 {
643                                         case 'P':
644                                         case 'p':
645                                                 RepairMode |= REINSTALLMODE_FILEMISSING;
646                                                 break;
647                                         case 'O':
648                                         case 'o':
649                                                 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
650                                                 break;
651                                         case 'E':
652                                         case 'e':
653                                                 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
654                                                 break;
655                                         case 'D':
656                                         case 'd':
657                                                 RepairMode |= REINSTALLMODE_FILEEXACT;
658                                                 break;
659                                         case 'C':
660                                         case 'c':
661                                                 RepairMode |= REINSTALLMODE_FILEVERIFY;
662                                                 break;
663                                         case 'A':
664                                         case 'a':
665                                                 RepairMode |= REINSTALLMODE_FILEREPLACE;
666                                                 break;
667                                         case 'U':
668                                         case 'u':
669                                                 RepairMode |= REINSTALLMODE_USERDATA;
670                                                 break;
671                                         case 'M':
672                                         case 'm':
673                                                 RepairMode |= REINSTALLMODE_MACHINEDATA;
674                                                 break;
675                                         case 'S':
676                                         case 's':
677                                                 RepairMode |= REINSTALLMODE_SHORTCUT;
678                                                 break;
679                                         case 'V':
680                                         case 'v':
681                                                 RepairMode |= REINSTALLMODE_PACKAGE;
682                                                 break;
683                                         default:
684                                                 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
685                                                 break;
686                                 }
687                         }
688                         if(len == 2)
689                         {
690                                 RepairMode = REINSTALLMODE_FILEMISSING |
691                                         REINSTALLMODE_FILEEQUALVERSION |
692                                         REINSTALLMODE_FILEVERIFY |
693                                         REINSTALLMODE_MACHINEDATA |
694                                         REINSTALLMODE_SHORTCUT;
695                         }
696                         i++;
697                         if(i >= argc)
698                                 ShowUsage(1);
699                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
700                         PackageName = argvW[i];
701                 }
702                 else if(msi_option_prefix(argvW[i], "x") || msi_option_equal(argvW[i], "uninstall"))
703                 {
704                         FunctionInstall = TRUE;
705                         if(msi_option_prefix(argvW[i], "x")) PackageName = argvW[i]+2;
706                         if(!PackageName || !PackageName[0])
707                         {
708                                 i++;
709                                 if (i >= argc)
710                                         ShowUsage(1);
711                                 PackageName = argvW[i];
712                         }
713                         WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName));
714                         StringListAppend(&property_list, RemoveAll);
715                 }
716                 else if(msi_option_prefix(argvW[i], "j"))
717                 {
718                         int j;
719                         int len = lstrlenW(argvW[i]);
720                         FunctionAdvertise = TRUE;
721                         for(j = 2; j < len; j++)
722                         {
723                                 switch(argvW[i][j])
724                                 {
725                                         case 'U':
726                                         case 'u':
727                                                 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
728                                                 break;
729                                         case 'M':
730                                         case 'm':
731                                                 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
732                                                 break;
733                                         default:
734                                                 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
735                                                 break;
736                                 }
737                         }
738                         i++;
739                         if(i >= argc)
740                                 ShowUsage(1);
741                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
742                         PackageName = argvW[i];
743                 }
744                 else if(msi_strequal(argvW[i], "u"))
745                 {
746                         FunctionAdvertise = TRUE;
747                         AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
748                         i++;
749                         if(i >= argc)
750                                 ShowUsage(1);
751                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
752                         PackageName = argvW[i];
753                 }
754                 else if(msi_strequal(argvW[i], "m"))
755                 {
756                         FunctionAdvertise = TRUE;
757                         AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
758                         i++;
759                         if(i >= argc)
760                                 ShowUsage(1);
761                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
762                         PackageName = argvW[i];
763                 }
764                 else if(msi_option_equal(argvW[i], "t"))
765                 {
766                         i++;
767                         if(i >= argc)
768                                 ShowUsage(1);
769                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
770                         StringListAppend(&transform_list, argvW[i]);
771                 }
772                 else if(msi_option_equal(argvW[i], "g"))
773                 {
774                         i++;
775                         if(i >= argc)
776                                 ShowUsage(1);
777                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
778                         Language = msi_atou(argvW[i]);
779                 }
780                 else if(msi_option_prefix(argvW[i], "l"))
781                 {
782                         int j;
783                         int len = lstrlenW(argvW[i]);
784                         for(j = 2; j < len; j++)
785                         {
786                                 switch(argvW[i][j])
787                                 {
788                                         case 'I':
789                                         case 'i':
790                                                 LogMode |= INSTALLLOGMODE_INFO;
791                                                 break;
792                                         case 'W':
793                                         case 'w':
794                                                 LogMode |= INSTALLLOGMODE_WARNING;
795                                                 break;
796                                         case 'E':
797                                         case 'e':
798                                                 LogMode |= INSTALLLOGMODE_ERROR;
799                                                 break;
800                                         case 'A':
801                                         case 'a':
802                                                 LogMode |= INSTALLLOGMODE_ACTIONSTART;
803                                                 break;
804                                         case 'R':
805                                         case 'r':
806                                                 LogMode |= INSTALLLOGMODE_ACTIONDATA;
807                                                 break;
808                                         case 'U':
809                                         case 'u':
810                                                 LogMode |= INSTALLLOGMODE_USER;
811                                                 break;
812                                         case 'C':
813                                         case 'c':
814                                                 LogMode |= INSTALLLOGMODE_COMMONDATA;
815                                                 break;
816                                         case 'M':
817                                         case 'm':
818                                                 LogMode |= INSTALLLOGMODE_FATALEXIT;
819                                                 break;
820                                         case 'O':
821                                         case 'o':
822                                                 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
823                                                 break;
824                                         case 'P':
825                                         case 'p':
826                                                 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
827                                                 break;
828                                         case 'V':
829                                         case 'v':
830                                                 LogMode |= INSTALLLOGMODE_VERBOSE;
831                                                 break;
832                                         case '*':
833                                                 LogMode = INSTALLLOGMODE_FATALEXIT |
834                                                         INSTALLLOGMODE_ERROR |
835                                                         INSTALLLOGMODE_WARNING |
836                                                         INSTALLLOGMODE_USER |
837                                                         INSTALLLOGMODE_INFO |
838                                                         INSTALLLOGMODE_RESOLVESOURCE |
839                                                         INSTALLLOGMODE_OUTOFDISKSPACE |
840                                                         INSTALLLOGMODE_ACTIONSTART |
841                                                         INSTALLLOGMODE_ACTIONDATA |
842                                                         INSTALLLOGMODE_COMMONDATA |
843                                                         INSTALLLOGMODE_PROPERTYDUMP |
844                                                         INSTALLLOGMODE_PROGRESS |
845                                                         INSTALLLOGMODE_INITIALIZE |
846                                                         INSTALLLOGMODE_TERMINATE |
847                                                         INSTALLLOGMODE_SHOWDIALOG;
848                                                 break;
849                                         case '+':
850                                                 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
851                                                 break;
852                                         case '!':
853                                                 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
854                                                 break;
855                                         default:
856                                                 break;
857                                 }
858                         }
859                         i++;
860                         if(i >= argc)
861                                 ShowUsage(1);
862                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
863                         LogFileName = argvW[i];
864                         if(MsiEnableLogW(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
865                         {
866                                 fprintf(stderr, "Logging in %s (0x%08x, %u) failed\n",
867                                          wine_dbgstr_w(LogFileName), LogMode, LogAttributes);
868                                 ExitProcess(1);
869                         }
870                 }
871                 else if(msi_option_equal(argvW[i], "p"))
872                 {
873                         FunctionPatch = TRUE;
874                         i++;
875                         if(i >= argc)
876                                 ShowUsage(1);
877                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
878                         PatchFileName = argvW[i];
879                 }
880                 else if(msi_option_prefix(argvW[i], "q"))
881                 {
882                         if(lstrlenW(argvW[i]) == 2 || msi_strequal(argvW[i]+2, "n") ||
883                            msi_strequal(argvW[i] + 2, "uiet"))
884                         {
885                                 InstallUILevel = INSTALLUILEVEL_NONE;
886                         }
887                         else if(msi_strequal(argvW[i]+2, "b"))
888                         {
889                                 InstallUILevel = INSTALLUILEVEL_BASIC;
890                         }
891                         else if(msi_strequal(argvW[i]+2, "r"))
892                         {
893                                 InstallUILevel = INSTALLUILEVEL_REDUCED;
894                         }
895                         else if(msi_strequal(argvW[i]+2, "f"))
896                         {
897                                 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
898                         }
899                         else if(msi_strequal(argvW[i]+2, "n+"))
900                         {
901                                 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
902                         }
903                         else if(msi_strequal(argvW[i]+2, "b+"))
904                         {
905                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
906                         }
907                         else if(msi_strequal(argvW[i]+2, "b-"))
908                         {
909                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
910                         }
911                         else if(msi_strequal(argvW[i]+2, "b+!"))
912                         {
913                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
914                                 WINE_FIXME("Unknown modifier: !\n");
915                         }
916                         else if(msi_strequal(argvW[i]+2, "b!"))
917                         {
918                                 InstallUILevel = INSTALLUILEVEL_BASIC;
919                                 WINE_FIXME("Unknown modifier: !\n");
920                         }
921                         else
922                         {
923                                 fprintf(stderr, "Unknown option \"%s\" for UI level\n",
924                                          wine_dbgstr_w(argvW[i]+2));
925                         }
926                 }
927                 else if(msi_option_equal(argvW[i], "y"))
928                 {
929                         FunctionDllRegisterServer = TRUE;
930                         i++;
931                         if(i >= argc)
932                                 ShowUsage(1);
933                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
934                         DllName = argvW[i];
935                 }
936                 else if(msi_option_equal(argvW[i], "z"))
937                 {
938                         FunctionDllUnregisterServer = TRUE;
939                         i++;
940                         if(i >= argc)
941                                 ShowUsage(1);
942                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
943                         DllName = argvW[i];
944                 }
945                 else if(msi_option_equal(argvW[i], "help") || msi_option_equal(argvW[i], "?"))
946                 {
947                         ShowUsage(0);
948                 }
949                 else if(msi_option_equal(argvW[i], "m"))
950                 {
951                         FunctionUnknown = TRUE;
952                         WINE_FIXME("Unknown parameter /m\n");
953                 }
954                 else if(msi_option_equal(argvW[i], "D"))
955                 {
956                         FunctionUnknown = TRUE;
957                         WINE_FIXME("Unknown parameter /D\n");
958                 }
959                 else if (msi_option_equal(argvW[i], "V"))
960                 {
961                     FunctionServer = TRUE;
962                 }
963                 else
964                         StringListAppend(&property_list, argvW[i]);
965         }
966
967         /* start the GUI */
968         MsiSetInternalUI(InstallUILevel, NULL);
969
970         Properties = build_properties( property_list );
971
972         if(FunctionInstallAdmin && FunctionPatch)
973                 FunctionInstall = FALSE;
974
975         ReturnCode = 1;
976         if(FunctionInstall)
977         {
978                 if(IsProductCode(PackageName))
979                         ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
980                 else
981                         ReturnCode = MsiInstallProductW(PackageName, Properties);
982         }
983         else if(FunctionRepair)
984         {
985                 if(IsProductCode(PackageName))
986                         WINE_FIXME("Product code treatment not implemented yet\n");
987                 else
988                         ReturnCode = MsiReinstallProductW(PackageName, RepairMode);
989         }
990         else if(FunctionAdvertise)
991         {
992                 LPWSTR Transforms = build_transforms( property_list );
993                 ReturnCode = MsiAdvertiseProductW(PackageName, (LPWSTR) AdvertiseMode, Transforms, Language);
994         }
995         else if(FunctionPatch)
996         {
997                 ReturnCode = MsiApplyPatchW(PatchFileName, PackageName, InstallType, Properties);
998         }
999         else if(FunctionDllRegisterServer)
1000         {
1001                 ReturnCode = DoDllRegisterServer(DllName);
1002         }
1003         else if(FunctionDllUnregisterServer)
1004         {
1005                 ReturnCode = DoDllUnregisterServer(DllName);
1006         }
1007         else if (FunctionRegServer)
1008         {
1009                 ReturnCode = DoRegServer();
1010         }
1011         else if (FunctionUnregServer)
1012         {
1013                 ReturnCode = DoUnregServer();
1014         }
1015         else if (FunctionServer)
1016         {
1017             ReturnCode = DoService();
1018         }
1019         else if (FunctionUnknown)
1020         {
1021                 WINE_FIXME( "Unknown function, ignoring\n" );
1022         }
1023         else
1024                 ShowUsage(1);
1025
1026         return ReturnCode;
1027 }