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