xref: /reactos/base/system/msiexec/msiexec.c (revision bae2bac6)
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 /* str1 is the same as str2, ignoring case */
220 static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
221 {
222 	DWORD len, ret;
223 	LPWSTR strW;
224 
225 	len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
226 	if( !len )
227 		return FALSE;
228 	if( lstrlenW(str1) != (len-1) )
229 		return FALSE;
230 	strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
231 	MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
232 	ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len);
233 	HeapFree(GetProcessHeap(), 0, strW);
234 	return (ret == CSTR_EQUAL);
235 }
236 
237 /* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
238 static BOOL msi_option_equal(LPCWSTR str1, LPCSTR str2)
239 {
240     if (str1[0] != '/' && str1[0] != '-')
241         return FALSE;
242 
243     /* skip over the hyphen or slash */
244     return msi_strequal(str1 + 1, str2);
245 }
246 
247 /* str2 is at the beginning of str1, ignoring case */
248 static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2)
249 {
250 	DWORD len, ret;
251 	LPWSTR strW;
252 
253 	len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
254 	if( !len )
255 		return FALSE;
256 	if( lstrlenW(str1) < (len-1) )
257 		return FALSE;
258 	strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
259 	MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
260 	ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1);
261 	HeapFree(GetProcessHeap(), 0, strW);
262 	return (ret == CSTR_EQUAL);
263 }
264 
265 /* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
266 static BOOL msi_option_prefix(LPCWSTR str1, LPCSTR str2)
267 {
268     if (str1[0] != '/' && str1[0] != '-')
269         return FALSE;
270 
271     /* skip over the hyphen or slash */
272     return msi_strprefix(str1 + 1, str2);
273 }
274 
275 static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
276 {
277 	VOID* (*proc)(void);
278 
279 	*DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
280 	if(!*DllHandle)
281 	{
282 		fprintf(stderr, "Unable to load dll %s\n", wine_dbgstr_w(DllName));
283 		ExitProcess(1);
284 	}
285 	proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
286 	if(!proc)
287 	{
288 		fprintf(stderr, "Dll %s does not implement function %s\n",
289 			wine_dbgstr_w(DllName), ProcName);
290 		FreeLibrary(*DllHandle);
291 		ExitProcess(1);
292 	}
293 
294 	return proc;
295 }
296 
297 static DWORD DoDllRegisterServer(LPCWSTR DllName)
298 {
299 	HRESULT hr;
300 	DLLREGISTERSERVER pfDllRegisterServer = NULL;
301 	HMODULE DllHandle = NULL;
302 
303 	pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
304 
305 	hr = pfDllRegisterServer();
306 	if(FAILED(hr))
307 	{
308 		fprintf(stderr, "Failed to register dll %s\n", wine_dbgstr_w(DllName));
309 		return 1;
310 	}
311 	printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
312 	if(DllHandle)
313 		FreeLibrary(DllHandle);
314 	return 0;
315 }
316 
317 static DWORD DoDllUnregisterServer(LPCWSTR DllName)
318 {
319 	HRESULT hr;
320 	DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
321 	HMODULE DllHandle = NULL;
322 
323 	pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
324 
325 	hr = pfDllUnregisterServer();
326 	if(FAILED(hr))
327 	{
328 		fprintf(stderr, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
329 		return 1;
330 	}
331 	printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
332 	if(DllHandle)
333 		FreeLibrary(DllHandle);
334 	return 0;
335 }
336 
337 static DWORD DoRegServer(void)
338 {
339     static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
340     static const WCHAR msiexecW[] = {'\\','m','s','i','e','x','e','c',' ','/','V',0};
341     SC_HANDLE scm, service;
342     WCHAR path[MAX_PATH+12];
343     DWORD len, ret = 0;
344 
345     if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CREATE_SERVICE)))
346     {
347         fprintf(stderr, "Failed to open the service control manager.\n");
348         return 1;
349     }
350     len = GetSystemDirectoryW(path, MAX_PATH);
351     lstrcpyW(path + len, msiexecW);
352     if ((service = CreateServiceW(scm, msiserverW, msiserverW, GENERIC_ALL,
353                                   SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START,
354                                   SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, NULL, NULL)))
355     {
356         CloseServiceHandle(service);
357     }
358     else if (GetLastError() != ERROR_SERVICE_EXISTS)
359     {
360         fprintf(stderr, "Failed to create MSI service\n");
361         ret = 1;
362     }
363     CloseServiceHandle(scm);
364     return ret;
365 }
366 
367 static DWORD DoUnregServer(void)
368 {
369     static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
370     SC_HANDLE scm, service;
371     DWORD ret = 0;
372 
373     if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CONNECT)))
374     {
375         fprintf(stderr, "Failed to open service control manager\n");
376         return 1;
377     }
378     if ((service = OpenServiceW(scm, msiserverW, DELETE)))
379     {
380         if (!DeleteService(service))
381         {
382             fprintf(stderr, "Failed to delete MSI service\n");
383             ret = 1;
384         }
385         CloseServiceHandle(service);
386     }
387     else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
388     {
389         fprintf(stderr, "Failed to open MSI service\n");
390         ret = 1;
391     }
392     CloseServiceHandle(scm);
393     return ret;
394 }
395 
396 static INT DoEmbedding( LPWSTR key )
397 {
398 	printf("Remote custom actions are not supported yet\n");
399 	return 1;
400 }
401 
402 /*
403  * state machine to break up the command line properly
404  */
405 
406 enum chomp_state
407 {
408     CS_WHITESPACE,
409     CS_TOKEN,
410     CS_QUOTE
411 };
412 
413 static int chomp( const WCHAR *in, WCHAR *out )
414 {
415     enum chomp_state state = CS_TOKEN;
416     const WCHAR *p;
417     int count = 1;
418     BOOL ignore;
419 
420     for (p = in; *p; p++)
421     {
422         ignore = TRUE;
423         switch (state)
424         {
425         case CS_WHITESPACE:
426             switch (*p)
427             {
428             case ' ':
429                 break;
430             case '"':
431                 state = CS_QUOTE;
432                 count++;
433                 break;
434             default:
435                 count++;
436                 ignore = FALSE;
437                 state = CS_TOKEN;
438             }
439             break;
440 
441         case CS_TOKEN:
442             switch (*p)
443             {
444             case '"':
445                 state = CS_QUOTE;
446                 break;
447             case ' ':
448                 state = CS_WHITESPACE;
449                 if (out) *out++ = 0;
450                 break;
451             default:
452                 if (p > in && p[-1] == '"')
453                 {
454                     if (out) *out++ = 0;
455                     count++;
456                 }
457                 ignore = FALSE;
458             }
459             break;
460 
461         case CS_QUOTE:
462             switch (*p)
463             {
464             case '"':
465                 state = CS_TOKEN;
466                 break;
467             default:
468                 ignore = FALSE;
469             }
470             break;
471         }
472         if (!ignore && out) *out++ = *p;
473     }
474     if (out) *out = 0;
475     return count;
476 }
477 
478 static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
479 {
480     WCHAR **argv, *p;
481     int i, count;
482 
483     *pargc = 0;
484     *pargv = NULL;
485 
486     count = chomp( cmdline, NULL );
487     if (!(p = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(cmdline) + count + 1) * sizeof(WCHAR) )))
488         return;
489 
490     count = chomp( cmdline, p );
491     if (!(argv = HeapAlloc( GetProcessHeap(), 0, (count + 1) * sizeof(WCHAR *) )))
492     {
493         HeapFree( GetProcessHeap(), 0, p );
494         return;
495     }
496     for (i = 0; i < count; i++)
497     {
498         argv[i] = p;
499         p += lstrlenW( p ) + 1;
500     }
501     argv[i] = NULL;
502 
503     *pargc = count;
504     *pargv = argv;
505 }
506 
507 static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***pargv )
508 {
509 	LONG r;
510 	HKEY hkey;
511 	DWORD sz = 0, type = 0;
512 	WCHAR *buf;
513 	BOOL ret = FALSE;
514 
515 	r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
516 	if(r != ERROR_SUCCESS)
517 		return FALSE;
518 	r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
519 	if(r == ERROR_SUCCESS && type == REG_SZ)
520 	{
521 		int len = lstrlenW( *pargv[0] );
522 		if (!(buf = HeapAlloc( GetProcessHeap(), 0, sz + (len + 1) * sizeof(WCHAR) )))
523 		{
524 			RegCloseKey( hkey );
525 			return FALSE;
526 		}
527 		memcpy( buf, *pargv[0], len * sizeof(WCHAR) );
528 		buf[len++] = ' ';
529 		r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)(buf + len), &sz);
530 		if( r == ERROR_SUCCESS )
531 		{
532 			process_args(buf, pargc, pargv);
533 			ret = TRUE;
534 		}
535 		HeapFree(GetProcessHeap(), 0, buf);
536 	}
537 	RegCloseKey(hkey);
538 	return ret;
539 }
540 
541 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
542 {
543 	int i;
544 	BOOL FunctionInstall = FALSE;
545 	BOOL FunctionInstallAdmin = FALSE;
546 	BOOL FunctionRepair = FALSE;
547 	BOOL FunctionAdvertise = FALSE;
548 	BOOL FunctionPatch = FALSE;
549 	BOOL FunctionDllRegisterServer = FALSE;
550 	BOOL FunctionDllUnregisterServer = FALSE;
551 	BOOL FunctionRegServer = FALSE;
552 	BOOL FunctionUnregServer = FALSE;
553 	BOOL FunctionServer = FALSE;
554 	BOOL FunctionUnknown = FALSE;
555 
556 	LPWSTR PackageName = NULL;
557 	LPWSTR Properties = NULL;
558 	struct string_list *property_list = NULL;
559 
560 	DWORD RepairMode = 0;
561 
562 	DWORD_PTR AdvertiseMode = 0;
563 	struct string_list *transform_list = NULL;
564 	LANGID Language = 0;
565 
566 	DWORD LogMode = 0;
567 	LPWSTR LogFileName = NULL;
568 	DWORD LogAttributes = 0;
569 
570 	LPWSTR PatchFileName = NULL;
571 	INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
572 
573 	INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
574 
575 	LPWSTR DllName = NULL;
576 	DWORD ReturnCode;
577 	int argc;
578 	LPWSTR *argvW = NULL;
579 
580 	/* parse the command line */
581 	process_args( GetCommandLineW(), &argc, &argvW );
582 
583 	/*
584 	 * If the args begin with /@ IDENT then we need to load the real
585 	 * command line out of the RunOnceEntries key in the registry.
586 	 *  We do that before starting to process the real commandline,
587 	 * then overwrite the commandline again.
588 	 */
589 	if(argc>1 && msi_option_equal(argvW[1], "@"))
590 	{
591 		if(!process_args_from_reg( argvW[2], &argc, &argvW ))
592 			return 1;
593 	}
594 
595 	if (argc == 3 && msi_option_equal(argvW[1], "Embedding"))
596 		return DoEmbedding( argvW[2] );
597 
598 	for(i = 1; i < argc; i++)
599 	{
600 		WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
601 
602 		if (msi_option_equal(argvW[i], "regserver"))
603 		{
604 			FunctionRegServer = TRUE;
605 		}
606 		else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")
607 			||  msi_option_equal(argvW[i], "unreg"))
608 		{
609 			FunctionUnregServer = TRUE;
610 		}
611 		else if(msi_option_prefix(argvW[i], "i") || msi_option_prefix(argvW[i], "package"))
612 		{
613 			LPWSTR argvWi = argvW[i];
614 			int argLen = (msi_option_prefix(argvW[i], "i") ? 2 : 8);
615 			FunctionInstall = TRUE;
616 			if(lstrlenW(argvW[i]) > argLen)
617 				argvWi += argLen;
618 			else
619 			{
620 				i++;
621 				if(i >= argc)
622 					ShowUsage(1);
623 				WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
624 				argvWi = argvW[i];
625 			}
626 			PackageName = argvWi;
627 		}
628 		else if(msi_option_equal(argvW[i], "a"))
629 		{
630 			FunctionInstall = TRUE;
631 			FunctionInstallAdmin = TRUE;
632 			InstallType = INSTALLTYPE_NETWORK_IMAGE;
633 			i++;
634 			if(i >= argc)
635 				ShowUsage(1);
636 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
637 			PackageName = argvW[i];
638 			StringListAppend(&property_list, ActionAdmin);
639 			WINE_FIXME("Administrative installs are not currently supported\n");
640 		}
641 		else if(msi_option_prefix(argvW[i], "f"))
642 		{
643 			int j;
644 			int len = lstrlenW(argvW[i]);
645 			FunctionRepair = TRUE;
646 			for(j = 2; j < len; j++)
647 			{
648 				switch(argvW[i][j])
649 				{
650 					case 'P':
651 					case 'p':
652 						RepairMode |= REINSTALLMODE_FILEMISSING;
653 						break;
654 					case 'O':
655 					case 'o':
656 						RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
657 						break;
658 					case 'E':
659 					case 'e':
660 						RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
661 						break;
662 					case 'D':
663 					case 'd':
664 						RepairMode |= REINSTALLMODE_FILEEXACT;
665 						break;
666 					case 'C':
667 					case 'c':
668 						RepairMode |= REINSTALLMODE_FILEVERIFY;
669 						break;
670 					case 'A':
671 					case 'a':
672 						RepairMode |= REINSTALLMODE_FILEREPLACE;
673 						break;
674 					case 'U':
675 					case 'u':
676 						RepairMode |= REINSTALLMODE_USERDATA;
677 						break;
678 					case 'M':
679 					case 'm':
680 						RepairMode |= REINSTALLMODE_MACHINEDATA;
681 						break;
682 					case 'S':
683 					case 's':
684 						RepairMode |= REINSTALLMODE_SHORTCUT;
685 						break;
686 					case 'V':
687 					case 'v':
688 						RepairMode |= REINSTALLMODE_PACKAGE;
689 						break;
690 					default:
691 						fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
692 						break;
693 				}
694 			}
695 			if(len == 2)
696 			{
697 				RepairMode = REINSTALLMODE_FILEMISSING |
698 					REINSTALLMODE_FILEEQUALVERSION |
699 					REINSTALLMODE_FILEVERIFY |
700 					REINSTALLMODE_MACHINEDATA |
701 					REINSTALLMODE_SHORTCUT;
702 			}
703 			i++;
704 			if(i >= argc)
705 				ShowUsage(1);
706 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
707 			PackageName = argvW[i];
708 		}
709 		else if(msi_option_prefix(argvW[i], "x") || msi_option_equal(argvW[i], "uninstall"))
710 		{
711 			FunctionInstall = TRUE;
712 			if(msi_option_prefix(argvW[i], "x")) PackageName = argvW[i]+2;
713 			if(!PackageName || !PackageName[0])
714 			{
715 				i++;
716 				if (i >= argc)
717 					ShowUsage(1);
718 				PackageName = argvW[i];
719 			}
720 			WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName));
721 			StringListAppend(&property_list, RemoveAll);
722 		}
723 		else if(msi_option_prefix(argvW[i], "j"))
724 		{
725 			int j;
726 			int len = lstrlenW(argvW[i]);
727 			FunctionAdvertise = TRUE;
728 			for(j = 2; j < len; j++)
729 			{
730 				switch(argvW[i][j])
731 				{
732 					case 'U':
733 					case 'u':
734 						AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
735 						break;
736 					case 'M':
737 					case 'm':
738 						AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
739 						break;
740 					default:
741 						fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
742 						break;
743 				}
744 			}
745 			i++;
746 			if(i >= argc)
747 				ShowUsage(1);
748 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
749 			PackageName = argvW[i];
750 		}
751 		else if(msi_strequal(argvW[i], "u"))
752 		{
753 			FunctionAdvertise = TRUE;
754 			AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
755 			i++;
756 			if(i >= argc)
757 				ShowUsage(1);
758 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
759 			PackageName = argvW[i];
760 		}
761 		else if(msi_strequal(argvW[i], "m"))
762 		{
763 			FunctionAdvertise = TRUE;
764 			AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
765 			i++;
766 			if(i >= argc)
767 				ShowUsage(1);
768 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
769 			PackageName = argvW[i];
770 		}
771 		else if(msi_option_equal(argvW[i], "t"))
772 		{
773 			i++;
774 			if(i >= argc)
775 				ShowUsage(1);
776 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
777 			StringListAppend(&transform_list, argvW[i]);
778 		}
779 		else if(msi_option_equal(argvW[i], "g"))
780 		{
781 			i++;
782 			if(i >= argc)
783 				ShowUsage(1);
784 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
785 			Language = msi_atou(argvW[i]);
786 		}
787 		else if(msi_option_prefix(argvW[i], "l"))
788 		{
789 			int j;
790 			int len = lstrlenW(argvW[i]);
791 			for(j = 2; j < len; j++)
792 			{
793 				switch(argvW[i][j])
794 				{
795 					case 'I':
796 					case 'i':
797 						LogMode |= INSTALLLOGMODE_INFO;
798 						break;
799 					case 'W':
800 					case 'w':
801 						LogMode |= INSTALLLOGMODE_WARNING;
802 						break;
803 					case 'E':
804 					case 'e':
805 						LogMode |= INSTALLLOGMODE_ERROR;
806 						break;
807 					case 'A':
808 					case 'a':
809 						LogMode |= INSTALLLOGMODE_ACTIONSTART;
810 						break;
811 					case 'R':
812 					case 'r':
813 						LogMode |= INSTALLLOGMODE_ACTIONDATA;
814 						break;
815 					case 'U':
816 					case 'u':
817 						LogMode |= INSTALLLOGMODE_USER;
818 						break;
819 					case 'C':
820 					case 'c':
821 						LogMode |= INSTALLLOGMODE_COMMONDATA;
822 						break;
823 					case 'M':
824 					case 'm':
825 						LogMode |= INSTALLLOGMODE_FATALEXIT;
826 						break;
827 					case 'O':
828 					case 'o':
829 						LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
830 						break;
831 					case 'P':
832 					case 'p':
833 						LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
834 						break;
835 					case 'V':
836 					case 'v':
837 						LogMode |= INSTALLLOGMODE_VERBOSE;
838 						break;
839 					case '*':
840 						LogMode = INSTALLLOGMODE_FATALEXIT |
841 							INSTALLLOGMODE_ERROR |
842 							INSTALLLOGMODE_WARNING |
843 							INSTALLLOGMODE_USER |
844 							INSTALLLOGMODE_INFO |
845 							INSTALLLOGMODE_RESOLVESOURCE |
846 							INSTALLLOGMODE_OUTOFDISKSPACE |
847 							INSTALLLOGMODE_ACTIONSTART |
848 							INSTALLLOGMODE_ACTIONDATA |
849 							INSTALLLOGMODE_COMMONDATA |
850 							INSTALLLOGMODE_PROPERTYDUMP |
851 							INSTALLLOGMODE_PROGRESS |
852 							INSTALLLOGMODE_INITIALIZE |
853 							INSTALLLOGMODE_TERMINATE |
854 							INSTALLLOGMODE_SHOWDIALOG;
855 						break;
856 					case '+':
857 						LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
858 						break;
859 					case '!':
860 						LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
861 						break;
862 					default:
863 						break;
864 				}
865 			}
866 			i++;
867 			if(i >= argc)
868 				ShowUsage(1);
869 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
870 			LogFileName = argvW[i];
871 			if(MsiEnableLogW(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
872 			{
873 				fprintf(stderr, "Logging in %s (0x%08x, %u) failed\n",
874 					 wine_dbgstr_w(LogFileName), LogMode, LogAttributes);
875 				ExitProcess(1);
876 			}
877 		}
878 		else if(msi_option_equal(argvW[i], "p") || msi_option_equal(argvW[i], "update"))
879 		{
880 			FunctionPatch = TRUE;
881 			i++;
882 			if(i >= argc)
883 				ShowUsage(1);
884 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
885 			PatchFileName = argvW[i];
886 		}
887 		else if(msi_option_prefix(argvW[i], "q"))
888 		{
889 			if(lstrlenW(argvW[i]) == 2 || msi_strequal(argvW[i]+2, "n") ||
890 			   msi_strequal(argvW[i] + 2, "uiet"))
891 			{
892 				InstallUILevel = INSTALLUILEVEL_NONE;
893 			}
894 			else if(msi_strequal(argvW[i]+2, "r"))
895 			{
896 				InstallUILevel = INSTALLUILEVEL_REDUCED;
897 			}
898 			else if(msi_strequal(argvW[i]+2, "f"))
899 			{
900 				InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
901 			}
902 			else if(msi_strequal(argvW[i]+2, "n+"))
903 			{
904 				InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
905 			}
906 			else if(msi_strprefix(argvW[i]+2, "b"))
907 			{
908                 const WCHAR *ptr = argvW[i] + 3;
909 
910                 InstallUILevel = INSTALLUILEVEL_BASIC;
911 
912                 while (*ptr)
913                 {
914                     if (msi_strprefix(ptr, "+"))
915                         InstallUILevel |= INSTALLUILEVEL_ENDDIALOG;
916                     if (msi_strprefix(ptr, "-"))
917                         InstallUILevel |= INSTALLUILEVEL_PROGRESSONLY;
918                     if (msi_strprefix(ptr, "!"))
919                     {
920                         WINE_FIXME("Unhandled modifier: !\n");
921                         InstallUILevel |= INSTALLUILEVEL_HIDECANCEL;
922                     }
923                     ptr++;
924                 }
925 			}
926 			else
927 			{
928 				fprintf(stderr, "Unknown option \"%s\" for UI level\n",
929 					 wine_dbgstr_w(argvW[i]+2));
930 			}
931 		}
932                 else if(msi_option_equal(argvW[i], "passive"))
933                 {
934                     static const WCHAR rebootpromptW[] =
935                         {'R','E','B','O','O','T','P','R','O','M','P','T','=','"','S','"',0};
936 
937                     InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY|INSTALLUILEVEL_HIDECANCEL;
938                     StringListAppend(&property_list, rebootpromptW);
939                 }
940 		else if(msi_option_equal(argvW[i], "y"))
941 		{
942 			FunctionDllRegisterServer = TRUE;
943 			i++;
944 			if(i >= argc)
945 				ShowUsage(1);
946 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
947 			DllName = argvW[i];
948 		}
949 		else if(msi_option_equal(argvW[i], "z"))
950 		{
951 			FunctionDllUnregisterServer = TRUE;
952 			i++;
953 			if(i >= argc)
954 				ShowUsage(1);
955 			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
956 			DllName = argvW[i];
957 		}
958 		else if(msi_option_equal(argvW[i], "help") || msi_option_equal(argvW[i], "?"))
959 		{
960 			ShowUsage(0);
961 		}
962 		else if(msi_option_equal(argvW[i], "m"))
963 		{
964 			FunctionUnknown = TRUE;
965 			WINE_FIXME("Unknown parameter /m\n");
966 		}
967 		else if(msi_option_equal(argvW[i], "D"))
968 		{
969 			FunctionUnknown = TRUE;
970 			WINE_FIXME("Unknown parameter /D\n");
971 		}
972 		else if (msi_option_equal(argvW[i], "V"))
973 		{
974 		    FunctionServer = TRUE;
975 		}
976 		else
977 			StringListAppend(&property_list, argvW[i]);
978 	}
979 
980 	/* start the GUI */
981 	MsiSetInternalUI(InstallUILevel, NULL);
982 
983 	Properties = build_properties( property_list );
984 
985 	if(FunctionInstallAdmin && FunctionPatch)
986 		FunctionInstall = FALSE;
987 
988 	ReturnCode = 1;
989 	if(FunctionInstall)
990 	{
991 		if(IsProductCode(PackageName))
992 			ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
993 		else
994 			ReturnCode = MsiInstallProductW(PackageName, Properties);
995 	}
996 	else if(FunctionRepair)
997 	{
998 		if(IsProductCode(PackageName))
999 			WINE_FIXME("Product code treatment not implemented yet\n");
1000 		else
1001 			ReturnCode = MsiReinstallProductW(PackageName, RepairMode);
1002 	}
1003 	else if(FunctionAdvertise)
1004 	{
1005 		LPWSTR Transforms = build_transforms( property_list );
1006 		ReturnCode = MsiAdvertiseProductW(PackageName, (LPWSTR) AdvertiseMode, Transforms, Language);
1007 	}
1008 	else if(FunctionPatch)
1009 	{
1010 		ReturnCode = MsiApplyPatchW(PatchFileName, PackageName, InstallType, Properties);
1011 	}
1012 	else if(FunctionDllRegisterServer)
1013 	{
1014 		ReturnCode = DoDllRegisterServer(DllName);
1015 	}
1016 	else if(FunctionDllUnregisterServer)
1017 	{
1018 		ReturnCode = DoDllUnregisterServer(DllName);
1019 	}
1020 	else if (FunctionRegServer)
1021 	{
1022 		ReturnCode = DoRegServer();
1023 	}
1024 	else if (FunctionUnregServer)
1025 	{
1026 		ReturnCode = DoUnregServer();
1027 	}
1028 	else if (FunctionServer)
1029 	{
1030 	    ReturnCode = DoService();
1031 	}
1032 	else if (FunctionUnknown)
1033 	{
1034 		WINE_FIXME( "Unknown function, ignoring\n" );
1035 	}
1036 	else
1037 		ShowUsage(1);
1038 
1039 	return ReturnCode;
1040 }
1041