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