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 } 1130