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