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