[MSIEXEC] Sync with Wine Staging 3.3. CORE-14434
[reactos.git] / base / system / msiexec / msiexec.c
1 /*
2 * msiexec.exe implementation
3 *
4 * Copyright 2004 Vincent BĂ©ron
5 * Copyright 2005 Mike McCormack
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #define WIN32_LEAN_AND_MEAN
23
24 #include <windows.h>
25 #include <msi.h>
26 #include <winsvc.h>
27 #include <objbase.h>
28 #include <stdio.h>
29
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
34
35 typedef HRESULT (WINAPI *DLLREGISTERSERVER)(void);
36 typedef HRESULT (WINAPI *DLLUNREGISTERSERVER)(void);
37
38 DWORD DoService(void);
39
40 struct string_list
41 {
42 struct string_list *next;
43 WCHAR str[1];
44 };
45
46 static const WCHAR ActionAdmin[] = {
47 'A','C','T','I','O','N','=','A','D','M','I','N',0 };
48 static const WCHAR RemoveAll[] = {
49 'R','E','M','O','V','E','=','A','L','L',0 };
50
51 static const WCHAR InstallRunOnce[] = {
52 'S','o','f','t','w','a','r','e','\\',
53 'M','i','c','r','o','s','o','f','t','\\',
54 'W','i','n','d','o','w','s','\\',
55 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
56 'I','n','s','t','a','l','l','e','r','\\',
57 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
58
59 static void ShowUsage(int ExitCode)
60 {
61 WCHAR msiexec_version[40];
62 WCHAR filename[MAX_PATH];
63 LPWSTR msi_res;
64 LPWSTR msiexec_help;
65 HMODULE hmsi = GetModuleHandleA("msi.dll");
66 DWORD len;
67 DWORD res;
68
69 /* MsiGetFileVersion need the full path */
70 *filename = 0;
71 res = GetModuleFileNameW(hmsi, filename, sizeof(filename) / sizeof(filename[0]));
72 if (!res)
73 WINE_ERR("GetModuleFileName failed: %d\n", GetLastError());
74
75 len = sizeof(msiexec_version) / sizeof(msiexec_version[0]);
76 *msiexec_version = 0;
77 res = MsiGetFileVersionW(filename, msiexec_version, &len, NULL, NULL);
78 if (res)
79 WINE_ERR("MsiGetFileVersion failed with %d\n", res);
80
81 /* Return the length of the resource.
82 No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
83 len = LoadStringW(hmsi, 10, (LPWSTR) &msi_res, 0);
84
85 msi_res = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
86 msiexec_help = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) + sizeof(msiexec_version));
87 if (msi_res && msiexec_help) {
88 *msi_res = 0;
89 LoadStringW(hmsi, 10, msi_res, len + 1);
90
91 sprintfW(msiexec_help, msi_res, msiexec_version);
92 MsiMessageBoxW(0, msiexec_help, NULL, 0, GetUserDefaultLangID(), 0);
93 }
94 HeapFree(GetProcessHeap(), 0, msi_res);
95 HeapFree(GetProcessHeap(), 0, msiexec_help);
96 ExitProcess(ExitCode);
97 }
98
99 static BOOL IsProductCode(LPWSTR str)
100 {
101 GUID ProductCode;
102
103 if(lstrlenW(str) != 38)
104 return FALSE;
105 return ( (CLSIDFromString(str, &ProductCode) == NOERROR) );
106
107 }
108
109 static VOID StringListAppend(struct string_list **list, LPCWSTR str)
110 {
111 struct string_list *entry;
112
113 entry = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(struct string_list, str[lstrlenW(str) + 1]));
114 if(!entry)
115 {
116 WINE_ERR("Out of memory!\n");
117 ExitProcess(1);
118 }
119 lstrcpyW(entry->str, str);
120 entry->next = NULL;
121
122 /*
123 * Ignoring o(n^2) time complexity to add n strings for simplicity,
124 * add the string to the end of the list to preserve the order.
125 */
126 while( *list )
127 list = &(*list)->next;
128 *list = entry;
129 }
130
131 static LPWSTR build_properties(struct string_list *property_list)
132 {
133 struct string_list *list;
134 LPWSTR ret, p, value;
135 DWORD len;
136 BOOL needs_quote;
137
138 if(!property_list)
139 return NULL;
140
141 /* count the space we need */
142 len = 1;
143 for(list = property_list; list; list = list->next)
144 len += lstrlenW(list->str) + 3;
145
146 ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
147
148 /* add a space before each string, and quote the value */
149 p = ret;
150 for(list = property_list; list; list = list->next)
151 {
152 value = strchrW(list->str,'=');
153 if(!value)
154 continue;
155 len = value - list->str;
156 *p++ = ' ';
157 memcpy(p, list->str, len * sizeof(WCHAR));
158 p += len;
159 *p++ = '=';
160
161 /* check if the value contains spaces and maybe quote it */
162 value++;
163 needs_quote = strchrW(value,' ') ? 1 : 0;
164 if(needs_quote)
165 *p++ = '"';
166 len = lstrlenW(value);
167 memcpy(p, value, len * sizeof(WCHAR));
168 p += len;
169 if(needs_quote)
170 *p++ = '"';
171 }
172 *p = 0;
173
174 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );
175
176 return ret;
177 }
178
179 static LPWSTR build_transforms(struct string_list *transform_list)
180 {
181 struct string_list *list;
182 LPWSTR ret, p;
183 DWORD len;
184
185 /* count the space we need */
186 len = 1;
187 for(list = transform_list; list; list = list->next)
188 len += lstrlenW(list->str) + 1;
189
190 ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
191
192 /* add all the transforms with a semicolon between each one */
193 p = ret;
194 for(list = transform_list; list; list = list->next)
195 {
196 len = lstrlenW(list->str);
197 lstrcpynW(p, list->str, len );
198 p += len;
199 if(list->next)
200 *p++ = ';';
201 }
202 *p = 0;
203
204 return ret;
205 }
206
207 static DWORD msi_atou(LPCWSTR str)
208 {
209 DWORD ret = 0;
210 while(*str >= '0' && *str <= '9')
211 {
212 ret *= 10;
213 ret += (*str - '0');
214 str++;
215 }
216 return ret;
217 }
218
219 /* 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 }