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