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