44f1f28862eabf2a547d7058827bbbf701d0ed7f
[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;
422 BOOL ignore;
423
424 for( p = str, out = str; *p; p++ )
425 {
426 ignore = TRUE;
427 switch( state )
428 {
429 case cs_whitespace:
430 switch( *p )
431 {
432 case ' ':
433 break;
434 case '"':
435 state = cs_quote;
436 count++;
437 break;
438 default:
439 count++;
440 ignore = FALSE;
441 state = cs_token;
442 }
443 break;
444
445 case cs_token:
446 switch( *p )
447 {
448 case '"':
449 state = cs_quote;
450 break;
451 case ' ':
452 state = cs_whitespace;
453 *out++ = 0;
454 break;
455 default:
456 ignore = FALSE;
457 }
458 break;
459
460 case cs_quote:
461 switch( *p )
462 {
463 case '"':
464 state = cs_token;
465 break;
466 default:
467 ignore = FALSE;
468 }
469 break;
470 }
471 if( !ignore )
472 *out++ = *p;
473 }
474
475 *out = 0;
476
477 return count;
478 }
479
480 static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
481 {
482 WCHAR **argv, *p = msi_strdup(cmdline);
483 int i, n;
484
485 n = chomp( p );
486 argv = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR*)*(n+1));
487 for( i=0; i<n; i++ )
488 {
489 argv[i] = p;
490 p += lstrlenW(p) + 1;
491 }
492 argv[i] = NULL;
493
494 *pargc = n;
495 *pargv = argv;
496 }
497
498 static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***pargv )
499 {
500 LONG r;
501 HKEY hkey;
502 DWORD sz = 0, type = 0;
503 WCHAR *buf;
504 BOOL ret = FALSE;
505
506 r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
507 if(r != ERROR_SUCCESS)
508 return FALSE;
509 r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
510 if(r == ERROR_SUCCESS && type == REG_SZ)
511 {
512 int len = lstrlenW( *pargv[0] );
513 if (!(buf = HeapAlloc( GetProcessHeap(), 0, sz + (len + 1) * sizeof(WCHAR) )))
514 {
515 RegCloseKey( hkey );
516 return FALSE;
517 }
518 memcpy( buf, *pargv[0], len * sizeof(WCHAR) );
519 buf[len++] = ' ';
520 r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)(buf + len), &sz);
521 if( r == ERROR_SUCCESS )
522 {
523 process_args(buf, pargc, pargv);
524 ret = TRUE;
525 }
526 HeapFree(GetProcessHeap(), 0, buf);
527 }
528 RegCloseKey(hkey);
529 return ret;
530 }
531
532 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
533 {
534 int i;
535 BOOL FunctionInstall = FALSE;
536 BOOL FunctionInstallAdmin = FALSE;
537 BOOL FunctionRepair = FALSE;
538 BOOL FunctionAdvertise = FALSE;
539 BOOL FunctionPatch = FALSE;
540 BOOL FunctionDllRegisterServer = FALSE;
541 BOOL FunctionDllUnregisterServer = FALSE;
542 BOOL FunctionRegServer = FALSE;
543 BOOL FunctionUnregServer = FALSE;
544 BOOL FunctionServer = FALSE;
545 BOOL FunctionUnknown = FALSE;
546
547 LPWSTR PackageName = NULL;
548 LPWSTR Properties = NULL;
549 struct string_list *property_list = NULL;
550
551 DWORD RepairMode = 0;
552
553 DWORD_PTR AdvertiseMode = 0;
554 struct string_list *transform_list = NULL;
555 LANGID Language = 0;
556
557 DWORD LogMode = 0;
558 LPWSTR LogFileName = NULL;
559 DWORD LogAttributes = 0;
560
561 LPWSTR PatchFileName = NULL;
562 INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
563
564 INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
565
566 LPWSTR DllName = NULL;
567 DWORD ReturnCode;
568 int argc;
569 LPWSTR *argvW = NULL;
570
571 /* parse the command line */
572 process_args( GetCommandLineW(), &argc, &argvW );
573
574 /*
575 * If the args begin with /@ IDENT then we need to load the real
576 * command line out of the RunOnceEntries key in the registry.
577 * We do that before starting to process the real commandline,
578 * then overwrite the commandline again.
579 */
580 if(argc>1 && msi_option_equal(argvW[1], "@"))
581 {
582 if(!process_args_from_reg( argvW[2], &argc, &argvW ))
583 return 1;
584 }
585
586 if (argc == 3 && msi_option_equal(argvW[1], "Embedding"))
587 return DoEmbedding( argvW[2] );
588
589 for(i = 1; i < argc; i++)
590 {
591 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
592
593 if (msi_option_equal(argvW[i], "regserver"))
594 {
595 FunctionRegServer = TRUE;
596 }
597 else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")
598 || msi_option_equal(argvW[i], "unreg"))
599 {
600 FunctionUnregServer = TRUE;
601 }
602 else if(msi_option_prefix(argvW[i], "i") || msi_option_prefix(argvW[i], "package"))
603 {
604 LPWSTR argvWi = argvW[i];
605 int argLen = (msi_option_prefix(argvW[i], "i") ? 2 : 8);
606 FunctionInstall = TRUE;
607 if(lstrlenW(argvW[i]) > argLen)
608 argvWi += argLen;
609 else
610 {
611 i++;
612 if(i >= argc)
613 ShowUsage(1);
614 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
615 argvWi = argvW[i];
616 }
617 PackageName = argvWi;
618 }
619 else if(msi_option_equal(argvW[i], "a"))
620 {
621 FunctionInstall = TRUE;
622 FunctionInstallAdmin = TRUE;
623 InstallType = INSTALLTYPE_NETWORK_IMAGE;
624 i++;
625 if(i >= argc)
626 ShowUsage(1);
627 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
628 PackageName = argvW[i];
629 StringListAppend(&property_list, ActionAdmin);
630 WINE_FIXME("Administrative installs are not currently supported\n");
631 }
632 else if(msi_option_prefix(argvW[i], "f"))
633 {
634 int j;
635 int len = lstrlenW(argvW[i]);
636 FunctionRepair = TRUE;
637 for(j = 2; j < len; j++)
638 {
639 switch(argvW[i][j])
640 {
641 case 'P':
642 case 'p':
643 RepairMode |= REINSTALLMODE_FILEMISSING;
644 break;
645 case 'O':
646 case 'o':
647 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
648 break;
649 case 'E':
650 case 'e':
651 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
652 break;
653 case 'D':
654 case 'd':
655 RepairMode |= REINSTALLMODE_FILEEXACT;
656 break;
657 case 'C':
658 case 'c':
659 RepairMode |= REINSTALLMODE_FILEVERIFY;
660 break;
661 case 'A':
662 case 'a':
663 RepairMode |= REINSTALLMODE_FILEREPLACE;
664 break;
665 case 'U':
666 case 'u':
667 RepairMode |= REINSTALLMODE_USERDATA;
668 break;
669 case 'M':
670 case 'm':
671 RepairMode |= REINSTALLMODE_MACHINEDATA;
672 break;
673 case 'S':
674 case 's':
675 RepairMode |= REINSTALLMODE_SHORTCUT;
676 break;
677 case 'V':
678 case 'v':
679 RepairMode |= REINSTALLMODE_PACKAGE;
680 break;
681 default:
682 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
683 break;
684 }
685 }
686 if(len == 2)
687 {
688 RepairMode = REINSTALLMODE_FILEMISSING |
689 REINSTALLMODE_FILEEQUALVERSION |
690 REINSTALLMODE_FILEVERIFY |
691 REINSTALLMODE_MACHINEDATA |
692 REINSTALLMODE_SHORTCUT;
693 }
694 i++;
695 if(i >= argc)
696 ShowUsage(1);
697 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
698 PackageName = argvW[i];
699 }
700 else if(msi_option_prefix(argvW[i], "x") || msi_option_equal(argvW[i], "uninstall"))
701 {
702 FunctionInstall = TRUE;
703 if(msi_option_prefix(argvW[i], "x")) PackageName = argvW[i]+2;
704 if(!PackageName || !PackageName[0])
705 {
706 i++;
707 if (i >= argc)
708 ShowUsage(1);
709 PackageName = argvW[i];
710 }
711 WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName));
712 StringListAppend(&property_list, RemoveAll);
713 }
714 else if(msi_option_prefix(argvW[i], "j"))
715 {
716 int j;
717 int len = lstrlenW(argvW[i]);
718 FunctionAdvertise = TRUE;
719 for(j = 2; j < len; j++)
720 {
721 switch(argvW[i][j])
722 {
723 case 'U':
724 case 'u':
725 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
726 break;
727 case 'M':
728 case 'm':
729 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
730 break;
731 default:
732 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
733 break;
734 }
735 }
736 i++;
737 if(i >= argc)
738 ShowUsage(1);
739 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
740 PackageName = argvW[i];
741 }
742 else if(msi_strequal(argvW[i], "u"))
743 {
744 FunctionAdvertise = TRUE;
745 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
746 i++;
747 if(i >= argc)
748 ShowUsage(1);
749 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
750 PackageName = argvW[i];
751 }
752 else if(msi_strequal(argvW[i], "m"))
753 {
754 FunctionAdvertise = TRUE;
755 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
756 i++;
757 if(i >= argc)
758 ShowUsage(1);
759 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
760 PackageName = argvW[i];
761 }
762 else if(msi_option_equal(argvW[i], "t"))
763 {
764 i++;
765 if(i >= argc)
766 ShowUsage(1);
767 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
768 StringListAppend(&transform_list, argvW[i]);
769 }
770 else if(msi_option_equal(argvW[i], "g"))
771 {
772 i++;
773 if(i >= argc)
774 ShowUsage(1);
775 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
776 Language = msi_atou(argvW[i]);
777 }
778 else if(msi_option_prefix(argvW[i], "l"))
779 {
780 int j;
781 int len = lstrlenW(argvW[i]);
782 for(j = 2; j < len; j++)
783 {
784 switch(argvW[i][j])
785 {
786 case 'I':
787 case 'i':
788 LogMode |= INSTALLLOGMODE_INFO;
789 break;
790 case 'W':
791 case 'w':
792 LogMode |= INSTALLLOGMODE_WARNING;
793 break;
794 case 'E':
795 case 'e':
796 LogMode |= INSTALLLOGMODE_ERROR;
797 break;
798 case 'A':
799 case 'a':
800 LogMode |= INSTALLLOGMODE_ACTIONSTART;
801 break;
802 case 'R':
803 case 'r':
804 LogMode |= INSTALLLOGMODE_ACTIONDATA;
805 break;
806 case 'U':
807 case 'u':
808 LogMode |= INSTALLLOGMODE_USER;
809 break;
810 case 'C':
811 case 'c':
812 LogMode |= INSTALLLOGMODE_COMMONDATA;
813 break;
814 case 'M':
815 case 'm':
816 LogMode |= INSTALLLOGMODE_FATALEXIT;
817 break;
818 case 'O':
819 case 'o':
820 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
821 break;
822 case 'P':
823 case 'p':
824 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
825 break;
826 case 'V':
827 case 'v':
828 LogMode |= INSTALLLOGMODE_VERBOSE;
829 break;
830 case '*':
831 LogMode = INSTALLLOGMODE_FATALEXIT |
832 INSTALLLOGMODE_ERROR |
833 INSTALLLOGMODE_WARNING |
834 INSTALLLOGMODE_USER |
835 INSTALLLOGMODE_INFO |
836 INSTALLLOGMODE_RESOLVESOURCE |
837 INSTALLLOGMODE_OUTOFDISKSPACE |
838 INSTALLLOGMODE_ACTIONSTART |
839 INSTALLLOGMODE_ACTIONDATA |
840 INSTALLLOGMODE_COMMONDATA |
841 INSTALLLOGMODE_PROPERTYDUMP |
842 INSTALLLOGMODE_PROGRESS |
843 INSTALLLOGMODE_INITIALIZE |
844 INSTALLLOGMODE_TERMINATE |
845 INSTALLLOGMODE_SHOWDIALOG;
846 break;
847 case '+':
848 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
849 break;
850 case '!':
851 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
852 break;
853 default:
854 break;
855 }
856 }
857 i++;
858 if(i >= argc)
859 ShowUsage(1);
860 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
861 LogFileName = argvW[i];
862 if(MsiEnableLogW(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
863 {
864 fprintf(stderr, "Logging in %s (0x%08x, %u) failed\n",
865 wine_dbgstr_w(LogFileName), LogMode, LogAttributes);
866 ExitProcess(1);
867 }
868 }
869 else if(msi_option_equal(argvW[i], "p") || msi_option_equal(argvW[i], "update"))
870 {
871 FunctionPatch = TRUE;
872 i++;
873 if(i >= argc)
874 ShowUsage(1);
875 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
876 PatchFileName = argvW[i];
877 }
878 else if(msi_option_prefix(argvW[i], "q"))
879 {
880 if(lstrlenW(argvW[i]) == 2 || msi_strequal(argvW[i]+2, "n") ||
881 msi_strequal(argvW[i] + 2, "uiet"))
882 {
883 InstallUILevel = INSTALLUILEVEL_NONE;
884 }
885 else if(msi_strequal(argvW[i]+2, "r"))
886 {
887 InstallUILevel = INSTALLUILEVEL_REDUCED;
888 }
889 else if(msi_strequal(argvW[i]+2, "f"))
890 {
891 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
892 }
893 else if(msi_strequal(argvW[i]+2, "n+"))
894 {
895 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
896 }
897 else if(msi_strprefix(argvW[i]+2, "b"))
898 {
899 const WCHAR *ptr = argvW[i] + 3;
900
901 InstallUILevel = INSTALLUILEVEL_BASIC;
902
903 while (*ptr)
904 {
905 if (msi_strprefix(ptr, "+"))
906 InstallUILevel |= INSTALLUILEVEL_ENDDIALOG;
907 if (msi_strprefix(ptr, "-"))
908 InstallUILevel |= INSTALLUILEVEL_PROGRESSONLY;
909 if (msi_strprefix(ptr, "!"))
910 {
911 WINE_FIXME("Unhandled modifier: !\n");
912 InstallUILevel |= INSTALLUILEVEL_HIDECANCEL;
913 }
914 ptr++;
915 }
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], "passive"))
924 {
925 static const WCHAR rebootpromptW[] =
926 {'R','E','B','O','O','T','P','R','O','M','P','T','=','"','S','"',0};
927
928 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY|INSTALLUILEVEL_HIDECANCEL;
929 StringListAppend(&property_list, rebootpromptW);
930 }
931 else if(msi_option_equal(argvW[i], "y"))
932 {
933 FunctionDllRegisterServer = TRUE;
934 i++;
935 if(i >= argc)
936 ShowUsage(1);
937 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
938 DllName = argvW[i];
939 }
940 else if(msi_option_equal(argvW[i], "z"))
941 {
942 FunctionDllUnregisterServer = 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], "help") || msi_option_equal(argvW[i], "?"))
950 {
951 ShowUsage(0);
952 }
953 else if(msi_option_equal(argvW[i], "m"))
954 {
955 FunctionUnknown = TRUE;
956 WINE_FIXME("Unknown parameter /m\n");
957 }
958 else if(msi_option_equal(argvW[i], "D"))
959 {
960 FunctionUnknown = TRUE;
961 WINE_FIXME("Unknown parameter /D\n");
962 }
963 else if (msi_option_equal(argvW[i], "V"))
964 {
965 FunctionServer = TRUE;
966 }
967 else
968 StringListAppend(&property_list, argvW[i]);
969 }
970
971 /* start the GUI */
972 MsiSetInternalUI(InstallUILevel, NULL);
973
974 Properties = build_properties( property_list );
975
976 if(FunctionInstallAdmin && FunctionPatch)
977 FunctionInstall = FALSE;
978
979 ReturnCode = 1;
980 if(FunctionInstall)
981 {
982 if(IsProductCode(PackageName))
983 ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
984 else
985 ReturnCode = MsiInstallProductW(PackageName, Properties);
986 }
987 else if(FunctionRepair)
988 {
989 if(IsProductCode(PackageName))
990 WINE_FIXME("Product code treatment not implemented yet\n");
991 else
992 ReturnCode = MsiReinstallProductW(PackageName, RepairMode);
993 }
994 else if(FunctionAdvertise)
995 {
996 LPWSTR Transforms = build_transforms( property_list );
997 ReturnCode = MsiAdvertiseProductW(PackageName, (LPWSTR) AdvertiseMode, Transforms, Language);
998 }
999 else if(FunctionPatch)
1000 {
1001 ReturnCode = MsiApplyPatchW(PatchFileName, PackageName, InstallType, Properties);
1002 }
1003 else if(FunctionDllRegisterServer)
1004 {
1005 ReturnCode = DoDllRegisterServer(DllName);
1006 }
1007 else if(FunctionDllUnregisterServer)
1008 {
1009 ReturnCode = DoDllUnregisterServer(DllName);
1010 }
1011 else if (FunctionRegServer)
1012 {
1013 ReturnCode = DoRegServer();
1014 }
1015 else if (FunctionUnregServer)
1016 {
1017 ReturnCode = DoUnregServer();
1018 }
1019 else if (FunctionServer)
1020 {
1021 ReturnCode = DoService();
1022 }
1023 else if (FunctionUnknown)
1024 {
1025 WINE_FIXME( "Unknown function, ignoring\n" );
1026 }
1027 else
1028 ShowUsage(1);
1029
1030 return ReturnCode;
1031 }