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