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 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 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 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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