/src/native/windows/setup/setup.c
C | 2046 lines | 1804 code | 104 blank | 138 comment | 157 complexity | ad350b093c1d592ed4d8708bac909621 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/* 2 * Jitsi, the OpenSource Java VoIP and Instant Messaging client. 3 * 4 * Distributable under LGPL license. 5 * See terms of license at gnu.org. 6 */ 7 8#include "lasterror.h" 9#include "nls.h" 10#include "registry.h" 11#include "setup.h" 12 13#include <ctype.h> /* isspace */ 14#include <stdint.h> /* intptr_t */ 15#include <stdlib.h> 16#include <string.h> 17#include <tchar.h> 18 19#include <objbase.h> 20#ifndef ERROR_RESOURCE_ENUM_USER_STOP 21#define ERROR_RESOURCE_ENUM_USER_STOP 0x3B02 22#endif /* #ifndef ERROR_RESOURCE_ENUM_USER_STOP */ 23#include <shellapi.h> 24#ifndef SEE_MASK_NOASYNC 25#define SEE_MASK_NOASYNC 0x00000100 26#endif /* #ifndef SEE_MASK_NOASYNC */ 27#include <tlhelp32.h> /* CreateToolhelp32Snapshot */ 28 29#include <bspatch.h> 30#include <lzma.h> 31 32#define SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR_PROPERTY_BEGIN \ 33 L"SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR=\"" 34 35static LPWSTR Setup_commandLine = NULL; 36static LPTSTR Setup_fileName = NULL; 37 38/** 39 * The indicator which determines whether this setup is to execute as msiexec 40 * only and is to just execute an MSI specified on the command line. 41 */ 42static BOOL Setup_msiexec_ = FALSE; 43static LPTSTR Setup_productName = NULL; 44 45/** 46 * The indicator which determines whether this setup is to display no user 47 * interface such as error message dialogs and the error status of the 48 * application is to be reported as its exit code. 49 */ 50static BOOL Setup_quiet = FALSE; 51static BOOL Setup_waitForParentProcess_ = FALSE; 52 53/** 54 * The indicator which determines whether this setup is to execute as xzdec only 55 * and is to just extract its payload in the current directory. 56 */ 57static BOOL Setup_xzdec_ = FALSE; 58 59BOOL CALLBACK Setup_enumResNameProc(HMODULE module, LPCTSTR type, LPTSTR name, LONG_PTR param); 60static DWORD Setup_executeBspatch(LPCTSTR path); 61static DWORD Setup_executeMsiA(LPCSTR path); 62static DWORD Setup_executeMsiW(LPCWSTR path); 63static DWORD Setup_extractAndExecutePayload(LPVOID ptr, DWORD size); 64#ifdef PACKAGECODE 65static LONG Setup_findLocalPackageByProductId(LPCTSTR productId, LPTSTR *localPackage); 66static LONG Setup_findProductIdByPackageCode(LPCTSTR packageCode, LPTSTR *productId); 67#endif /* #ifdef PACKAGECODE */ 68static void Setup_fixCommandLineQuotes(); 69static LPWSTR Setup_getArgW(LPWSTR commandLine, LPWSTR *value); 70static LPSTR Setup_getBoolArgA(LPCSTR argName, LPSTR commandLine, BOOL *boolValue); 71static LPWSTR Setup_getBoolArgW(LPCWSTR argName, LPWSTR commandLine, BOOL *boolValue); 72static LPCTSTR Setup_getFileName(); 73static DWORD Setup_getParentProcess(DWORD *ppid, LPTSTR *fileName); 74static LPCTSTR Setup_getProductName(); 75static DWORD Setup_getWinMainCmdLine(LPTSTR *winMainCmdLine); 76static int Setup_isWow64Acceptable(); 77LRESULT CALLBACK Setup_isWow64AcceptableMessageBoxCallWndRetProc(int code, WPARAM wParam, LPARAM lParam); 78static DWORD Setup_msiexec(); 79static DWORD Setup_parseCommandLine(LPTSTR cmdLine); 80static LPSTR Setup_skipWhitespaceA(LPSTR str); 81static LPWSTR Setup_skipWhitespaceW(LPWSTR str); 82static DWORD Setup_terminateUp2DateExe(); 83static DWORD Setup_waitForParentProcess(); 84static DWORD Setup_xzdec(LPVOID ptr, DWORD size, HANDLE file); 85 86#ifdef _UNICODE 87#define Setup_executeMsi(path) \ 88 Setup_executeMsiW(path) 89#define Setup_getBoolArg(argName, commandLine, boolValue) \ 90 Setup_getBoolArgW(argName, commandLine, boolValue) 91#define Setup_skipWhitespace(str) \ 92 Setup_skipWhitespaceW(str) 93#else /* #ifdef _UNICODE */ 94#define Setup_executeMsi(path) \ 95 Setup_executeMsiA(path) 96#define Setup_getBoolArg(argName, commandLine, boolValue) \ 97 Setup_getBoolArgA(argName, commandLine, boolValue) 98#define Setup_skipWhitespace(str) \ 99 Setup_skipWhitespaceA(str) 100#endif /* #ifdef _UNICODE */ 101 102BOOL CALLBACK 103Setup_enumResNameProc( 104 HMODULE module, 105 LPCTSTR type, LPTSTR name, 106 LONG_PTR param) 107{ 108 HRSRC rsrc = FindResource(module, name, type); 109 BOOL proceed = TRUE; 110 DWORD error = ERROR_SUCCESS; 111 112 if (rsrc) 113 { 114 DWORD size = SizeofResource(module, rsrc); 115 116 if (size) 117 { 118 HGLOBAL global = LoadResource(module, rsrc); 119 120 if (global) 121 { 122 LPVOID ptr = LockResource(global); 123 124 if (ptr) 125 { 126 proceed = FALSE; 127 error = Setup_extractAndExecutePayload(ptr, size); 128 } 129 else 130 { 131 error = GetLastError(); 132 LastError_setLastError(error, _T(__FILE__), __LINE__); 133 } 134 } 135 else 136 { 137 error = GetLastError(); 138 LastError_setLastError(error, _T(__FILE__), __LINE__); 139 } 140 } 141 else 142 { 143 error = GetLastError(); 144 LastError_setLastError(error, _T(__FILE__), __LINE__); 145 } 146 } 147 else 148 { 149 error = GetLastError(); 150 LastError_setLastError(error, _T(__FILE__), __LINE__); 151 } 152 if (param) 153 *((DWORD *) param) = error; 154 return proceed; 155} 156 157static DWORD 158Setup_executeBspatch(LPCTSTR path) 159{ 160 DWORD error; 161 162#ifdef PACKAGECODE 163 LPTSTR packageCode = _tcsdup(PACKAGECODE); 164 165 if (packageCode) 166 { 167 /* 168 * Strip the display characters from the GUID, only its bytes are 169 * important. 170 */ 171 size_t i; 172 size_t j; 173 size_t packageCodeLength = _tcslen(packageCode); 174 175 for (i = 0, j = 0; i < packageCodeLength; i++) 176 { 177 TCHAR c = packageCode[i]; 178 179 if ((_T('{') != c) && (_T('}') != c) && (_T('-') != c)) 180 packageCode[j++] = c; 181 } 182 packageCode[j] = 0; 183 packageCodeLength = j; 184 185 if (32 == packageCodeLength) 186 { 187 TCHAR swap; 188 LPTSTR pc = packageCode; 189 LPTSTR productId; 190 191 /* 8 */ 192 swap = pc[7]; pc[7] = pc[0]; pc[0] = swap; 193 swap = pc[6]; pc[6] = pc[1]; pc[1] = swap; 194 swap = pc[5]; pc[5] = pc[2]; pc[2] = swap; 195 swap = pc[4]; pc[4] = pc[3]; pc[3] = swap; 196 /* 4 */ 197 swap = pc[11]; pc[11] = pc[8]; pc[8] = swap; 198 swap = pc[10]; pc[10] = pc[9]; pc[9] = swap; 199 /* 4 */ 200 swap = pc[15]; pc[15] = pc[12]; pc[12] = swap; 201 swap = pc[14]; pc[14] = pc[13]; pc[13] = swap; 202 /* 4 */ 203 swap = pc[17]; pc[17] = pc[16]; pc[16] = swap; 204 swap = pc[19]; pc[19] = pc[18]; pc[18] = swap; 205 /* 12 */ 206 swap = pc[21]; pc[21] = pc[20]; pc[20] = swap; 207 swap = pc[23]; pc[23] = pc[22]; pc[22] = swap; 208 swap = pc[25]; pc[25] = pc[24]; pc[24] = swap; 209 swap = pc[27]; pc[27] = pc[26]; pc[26] = swap; 210 swap = pc[29]; pc[29] = pc[28]; pc[28] = swap; 211 swap = pc[31]; pc[31] = pc[30]; pc[30] = swap; 212 213 error = Setup_findProductIdByPackageCode(packageCode, &productId); 214 if (ERROR_SUCCESS == error) 215 { 216 LPTSTR localPackage; 217 218 error 219 = Setup_findLocalPackageByProductId( 220 productId, 221 &localPackage); 222#ifdef PACKAGESIZE 223 /* 224 * Windows Installer on Windows XP caches the MSI database only 225 * so the localPackage cannot really be used with bspatch as the 226 * old file to produce the new file. Unfortunately, bspatch will 227 * report that it has successfully produced the new file from 228 * the old file in this scenario but the resulting MSI will be 229 * malformed. As a workaround to detect this error, make sure 230 * that the localPackage is with the expected size in bytes. 231 */ 232 if (ERROR_SUCCESS == error) 233 { 234 HANDLE hLocalPackage 235 = CreateFile( 236 localPackage, 237 GENERIC_READ, 238 FILE_SHARE_READ, 239 NULL, 240 OPEN_EXISTING, 241 0, 242 NULL); 243 244 if (INVALID_HANDLE_VALUE == hLocalPackage) 245 { 246 error = GetLastError(); 247 LastError_setLastError(error, _T(__FILE__), __LINE__); 248 } 249 else 250 { 251 LARGE_INTEGER packageSize; 252 253 if (GetFileSizeEx(hLocalPackage, &packageSize)) 254 { 255 if (PACKAGESIZE != packageSize.QuadPart) 256 { 257 error = ERROR_FILE_NOT_FOUND; 258 LastError_setLastError( 259 error, 260 _T(__FILE__), __LINE__); 261 } 262 } 263 else 264 { 265 error = GetLastError(); 266 LastError_setLastError(error, _T(__FILE__), __LINE__); 267 } 268 CloseHandle(hLocalPackage); 269 } 270 } 271#endif /* #ifdef PACKAGESIZE */ 272 if (ERROR_SUCCESS == error) 273 { 274 /* 275 * The path to the new file to be produced by bspatch.exe is 276 * optional. If it is not specified on the command line, 277 * default to a path derived from the path to the .bspatch 278 * file. 279 */ 280 LPWSTR wNewPath; 281 LPTSTR newPath; 282 283 Setup_commandLine 284 = Setup_getArgW(Setup_commandLine, &wNewPath); 285 if (wNewPath) 286 { 287#ifdef _UNICODE 288 newPath = wNewPath; 289#else /* #ifdef _UNICODE */ 290 newPath = NLS_wstr2str(wNewPath); 291#endif /* #ifdef _UNICODE */ 292 } 293 else 294 { 295 size_t pathLength = _tcslen(path); 296 LPCTSTR extension = _T(".msi"); 297 size_t extensionLength = _tcslen(extension); 298 299 newPath 300 = malloc( 301 sizeof(TCHAR) 302 * (pathLength + extensionLength + 1)); 303 if (newPath) 304 { 305 LPTSTR str; 306 307 str = newPath; 308 _tcsncpy(str, path, pathLength); 309 str += pathLength; 310 _tcsncpy(str, extension, extensionLength); 311 str += extensionLength; 312 *str = 0; 313 } 314 } 315 /* 316 * Execute bspatch.exe (or rather the function it has been 317 * compiled into). 318 */ 319 if (newPath) 320 { 321 LPCTSTR argv[] 322 = { 323 _T("bspatch.exe"), 324 localPackage, newPath, path, 325 NULL 326 }; 327 328 if (bspatch_main( 329 (sizeof(argv) / sizeof(LPCTSTR)) - 1, 330 argv)) 331 { 332 error = ERROR_GEN_FAILURE; 333 LastError_setLastError(error, _T(__FILE__), __LINE__); 334 } 335 if (((LPVOID) newPath) != ((LPVOID) wNewPath)) 336 free(newPath); 337 } 338 else 339 { 340 error = ERROR_NOT_ENOUGH_MEMORY; 341 LastError_setLastError(error, _T(__FILE__), __LINE__); 342 } 343 free(localPackage); 344 } 345 free(productId); 346 } 347 } 348 else 349 { 350 error = ERROR_INVALID_PARAMETER; 351 LastError_setLastError(error, _T(__FILE__), __LINE__); 352 } 353 free(packageCode); 354 } 355 else 356 { 357 error = ERROR_NOT_ENOUGH_MEMORY; 358 LastError_setLastError(error, _T(__FILE__), __LINE__); 359 } 360#else /* #ifdef PACKAGECODE */ 361 error = ERROR_CALL_NOT_IMPLEMENTED; 362 LastError_setLastError(error, _T(__FILE__), __LINE__); 363#endif /* #ifdef PACKAGECODE */ 364 return error; 365} 366 367static DWORD 368Setup_executeMsiA(LPCSTR path) 369{ 370 LPWSTR wpath = NLS_str2wstr(path); 371 DWORD error; 372 373 if (wpath) 374 { 375 error = Setup_executeMsiW(wpath); 376 free(wpath); 377 } 378 else 379 { 380 error = ERROR_OUTOFMEMORY; 381 LastError_setLastError(error, _T(__FILE__), __LINE__); 382 } 383 return error; 384} 385 386static DWORD 387Setup_executeMsiW(LPCWSTR path) 388{ 389 DWORD error = ERROR_SUCCESS; 390 LPCWSTR p0, p1, p2, p3; 391 size_t p0Length, p1Length, p2Length, p3Length; 392 LPWSTR parameters; 393 394 p0 = L"/i \""; 395 p0Length = wcslen(p0); 396 p1 = path; 397 if (p1) 398 p1Length = wcslen(p1); 399 else 400 { 401 error = ERROR_INVALID_PARAMETER; 402 LastError_setLastError(error, _T(__FILE__), __LINE__); 403 return error; 404 } 405 p2 = L"\" REINSTALLMODE=amus "; 406 p2Length = wcslen(p2); 407 p3 = Setup_commandLine; 408 p3Length = p3 ? wcslen(p3) : 0; 409 410 parameters 411 = (LPWSTR) 412 malloc( 413 sizeof(wchar_t) 414 * (p0Length + p1Length + p2Length + p3Length + 1)); 415 if (parameters) 416 { 417 LPWSTR str = parameters; 418 SHELLEXECUTEINFOW sei; 419 420 wcsncpy(str, p0, p0Length); 421 str += p0Length; 422 wcsncpy(str, p1, p1Length); 423 str += p1Length; 424 wcsncpy(str, p2, p2Length); 425 str += p2Length; 426 if (p3Length) 427 { 428 wcsncpy(str, p3, p3Length); 429 str += p3Length; 430 } 431 *str = 0; 432 433 ZeroMemory(&sei, sizeof(sei)); 434 sei.cbSize = sizeof(sei); 435 sei.fMask 436 = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI; 437 sei.lpVerb = L"open"; 438 sei.lpFile = L"msiexec.exe"; 439 sei.lpParameters = parameters; 440 sei.nShow = SW_SHOWNORMAL; 441 442 /* 443 * MSDN says it is good practice to always initialize COM before using 444 * ShellExecuteEx. 445 */ 446 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 447 448 if (ShellExecuteExW(&sei) && (((intptr_t) (sei.hInstApp)) > 32)) 449 { 450 if (sei.hProcess) 451 { 452 DWORD event; 453 454 do 455 { 456 event = WaitForSingleObject(sei.hProcess, INFINITE); 457 if (WAIT_FAILED == event) 458 { 459 error = GetLastError(); 460 LastError_setLastError(error, _T(__FILE__), __LINE__); 461 break; 462 } 463 } 464 while (WAIT_TIMEOUT == event); 465 CloseHandle(sei.hProcess); 466 } 467 } 468 else 469 { 470 error = GetLastError(); 471 LastError_setLastError(error, _T(__FILE__), __LINE__); 472 } 473 474 free(parameters); 475 } 476 else 477 { 478 error = ERROR_OUTOFMEMORY; 479 LastError_setLastError(error, _T(__FILE__), __LINE__); 480 } 481 return error; 482} 483 484static DWORD 485Setup_extractAndExecutePayload(LPVOID ptr, DWORD size) 486{ 487 TCHAR path[MAX_PATH + 1]; 488 DWORD pathSize = sizeof(path) / sizeof(TCHAR); 489 DWORD tempPathLength; 490 DWORD error = ERROR_SUCCESS; 491 492 /* 493 * When this application is to execute in the fashion of xzdec only, it does 494 * not sound like a nice idea to extract its payload in the TEMP directory 495 * and the current directory sounds like an acceptable compromise (given 496 * that it is far more complex to accept the path to extract to as a command 497 * line argument). 498 */ 499 if (Setup_xzdec_) 500 { 501 path[0] = _T('.'); 502 path[1] = _T('\\'); 503 /* 504 * It is not necessary to null-terminate path because it will 505 * automatically be done later in accord with the value of 506 * tempPathLength. 507 */ 508 tempPathLength = 2; 509 } 510 else 511 tempPathLength = GetTempPath(pathSize, path); 512 if (tempPathLength) 513 { 514 if (tempPathLength > pathSize) 515 { 516 error = ERROR_NOT_ENOUGH_MEMORY; 517 LastError_setLastError(error, _T(__FILE__), __LINE__); 518 } 519 else 520 { 521 LPCTSTR fileName = Setup_getFileName(); 522 HANDLE file = INVALID_HANDLE_VALUE; 523 524 if (fileName) 525 { 526 size_t fileNameLength = _tcslen(fileName); 527 size_t freePathSize; 528#ifdef PACKAGECODE 529 LPCTSTR fileType = _T("bspatch"); 530 BOOL xzdec = FALSE; 531#else /* #ifdef PACKAGECODE */ 532 LPCTSTR fileType = _T("msi"); 533 BOOL xzdec = TRUE; 534#endif /* #ifdef PACKAGECODE */ 535 536 if ((fileNameLength > 4 /* .exe */) 537 && ((freePathSize 538 = (pathSize 539 - (tempPathLength 540 + fileNameLength 541 + 1))) 542 >= 0)) 543 { 544 LPTSTR str = path + tempPathLength; 545 size_t fileTypeLength = _tcslen(fileType); 546 547 _tcsncpy(str, fileName, fileNameLength - 4); 548 str += (fileNameLength - 4); 549 *str = _T('.'); 550 str++; 551 _tcsncpy(str, fileType, 3); 552 str += 3; 553 554 /* 555 * If possible, use the whole fileType for the extension and 556 * not just its first 3 characters. 557 */ 558 fileTypeLength -= 3; 559 if ((fileTypeLength > 0) 560 && (fileTypeLength <= freePathSize)) 561 { 562 _tcsncpy(str, fileType + 3, fileTypeLength); 563 str += fileTypeLength; 564 } 565 566 *str = 0; 567 568 file 569 = CreateFile( 570 path, 571 GENERIC_WRITE, 572 0, 573 NULL, 574 CREATE_NEW, 575 FILE_ATTRIBUTE_TEMPORARY, 576 NULL); 577 } 578 579 if (INVALID_HANDLE_VALUE == file) 580 { 581 LPTSTR tempPath; 582 583 path[tempPathLength] = 0; 584 tempPath = _tcsdup(path); 585 586 if (tempPath) 587 { 588 if (0 589 == GetTempFileName( 590 tempPath, 591 fileType, 592 0, 593 path)) 594 { 595 error = GetLastError(); 596 LastError_setLastError(error, _T(__FILE__), __LINE__); 597 } 598 else 599 { 600 file 601 = CreateFile( 602 path, 603 GENERIC_WRITE, 604 0, 605 NULL, 606 CREATE_ALWAYS, 607 FILE_ATTRIBUTE_TEMPORARY, 608 NULL); 609 if (INVALID_HANDLE_VALUE == file) 610 { 611 error = GetLastError(); 612 LastError_setLastError( 613 error, 614 _T(__FILE__), __LINE__); 615 } 616 } 617 free(tempPath); 618 } 619 else 620 { 621 error = ERROR_OUTOFMEMORY; 622 LastError_setLastError(error, _T(__FILE__), __LINE__); 623 } 624 } 625 626 if (INVALID_HANDLE_VALUE != file) 627 { 628 if (xzdec) 629 error = Setup_xzdec(ptr, size, file); 630 else 631 { 632 DWORD numberOfBytesWritten; 633 634 if (!WriteFile( 635 file, 636 ptr, size, 637 &numberOfBytesWritten, 638 NULL)) 639 { 640 error = GetLastError(); 641 LastError_setLastError(error, _T(__FILE__), __LINE__); 642 } 643 } 644 645 /* When executing as xzdec, do not execute the MSI. */ 646 if ((ERROR_SUCCESS == error) && !Setup_xzdec_) 647 { 648 if (Setup_waitForParentProcess_) 649 Setup_waitForParentProcess(); 650 651 CloseHandle(file); 652 if (_tcsnicmp(_T("bspatch"), fileType, 3) == 0) 653 error = Setup_executeBspatch(path); 654 else if (_tcsnicmp(_T("msi"), fileType, 3) == 0) 655 error = Setup_executeMsi(path); 656 else 657 { 658 error = ERROR_CALL_NOT_IMPLEMENTED; 659 LastError_setLastError(error, _T(__FILE__), __LINE__); 660 } 661 } 662 else 663 CloseHandle(file); 664 /* 665 * Delete the MSI if executing as setup or if executing as 666 * xzdec and the extraction has failed (in the fashion of 667 * other popular decompressors). 668 */ 669 if (!Setup_xzdec_ || (ERROR_SUCCESS != error)) 670 DeleteFile(path); 671 } 672 } 673 } 674 } 675 else 676 { 677 error = GetLastError(); 678 LastError_setLastError(error, _T(__FILE__), __LINE__); 679 } 680 return error; 681} 682 683#ifdef PACKAGECODE 684static LONG 685Setup_findLocalPackageByProductId(LPCTSTR productId, LPTSTR *localPackage) 686{ 687 HKEY userDataKey; 688 LONG error 689 = RegOpenKeyEx( 690 HKEY_LOCAL_MACHINE, 691 _T("Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData"), 692 0, 693 KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY, 694 &userDataKey); 695 696 if (ERROR_SUCCESS == error) 697 { 698 TCHAR userDataSubKeyName[1024]; 699 const DWORD userDataSubKeyNameCapacity 700 = sizeof(userDataSubKeyName) / sizeof(TCHAR); 701 DWORD index = 0; 702 LPCTSTR products = _T("\\Products\\"); 703 const DWORD productsLength = 10; 704 const DWORD productIdLength = 32; 705 LPCTSTR installProperties = _T("\\InstallProperties"); 706 const DWORD installPropertiesLength = 18; 707 TCHAR installPropertiesKeyName[ 708 userDataSubKeyNameCapacity 709 + productsLength 710 + productIdLength 711 + installPropertiesLength]; 712 713 *localPackage = NULL; 714 do 715 { 716 DWORD userDataSubKeyNameLength = userDataSubKeyNameCapacity; 717 LPTSTR str; 718 HKEY installPropertiesKey; 719 720 error 721 = RegEnumKeyEx( 722 userDataKey, 723 index, 724 userDataSubKeyName, &userDataSubKeyNameLength, 725 NULL, 726 NULL, NULL, 727 NULL); 728 index++; 729 if (ERROR_MORE_DATA == error) 730 continue; 731 if (ERROR_SUCCESS != error) 732 { 733 LastError_setLastError(error, _T(__FILE__), __LINE__); 734 break; 735 } 736 737 str = installPropertiesKeyName; 738 _tcsncpy(str, userDataSubKeyName, userDataSubKeyNameLength); 739 str += userDataSubKeyNameLength; 740 _tcsncpy(str, products, productsLength); 741 str += productsLength; 742 _tcsncpy(str, productId, productIdLength); 743 str += productIdLength; 744 _tcsncpy(str, installProperties, installPropertiesLength); 745 str += installPropertiesLength; 746 *str = 0; 747 748 error 749 = RegOpenKeyEx( 750 userDataKey, 751 installPropertiesKeyName, 752 0, 753 KEY_QUERY_VALUE | KEY_WOW64_64KEY, 754 &installPropertiesKey); 755 if (ERROR_SUCCESS == error) 756 { 757 error 758 = Run_getRegSzValue( 759 installPropertiesKey, 760 _T("LocalPackage"), 761 localPackage); 762 /* 763 * Reset the value stored at localPackage to NULL in case 764 * Run_getRegSzValue has failed but has assigned an invalid 765 * value prior to the failure. 766 */ 767 if (ERROR_SUCCESS != error) 768 *localPackage = NULL; 769 RegCloseKey(installPropertiesKey); 770 } 771 } 772 while (!(*localPackage)); 773 RegCloseKey(userDataKey); 774 if ((ERROR_SUCCESS == error) && !(*localPackage)) 775 { 776 error = ERROR_FILE_NOT_FOUND; 777 LastError_setLastError(error, _T(__FILE__), __LINE__); 778 } 779 } 780 else 781 LastError_setLastError(error, _T(__FILE__), __LINE__); 782 return error; 783} 784#endif /* #ifdef PACKAGECODE */ 785 786#ifdef PACKAGECODE 787static LONG 788Setup_findProductIdByPackageCode(LPCTSTR packageCode, LPTSTR *productId) 789{ 790 HKEY key; 791 LONG error 792 = RegOpenKeyEx( 793 HKEY_LOCAL_MACHINE, 794 _T("Software\\Classes\\Installer\\Products"), 795 0, 796 KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY, 797 &key); 798 799 if (ERROR_SUCCESS == error) 800 { 801 TCHAR subKeyName[33]; 802 const DWORD subKeyNameCapacity = sizeof(subKeyName) / sizeof(TCHAR); 803 DWORD index = 0; 804 805 *productId = NULL; 806 do 807 { 808 DWORD subKeyNameLength = subKeyNameCapacity; 809 HKEY subKey; 810 LPTSTR packageCodeOfProductId; 811 812 error 813 = RegEnumKeyEx( 814 key, 815 index, 816 subKeyName, &subKeyNameLength, 817 NULL, 818 NULL, NULL, 819 NULL); 820 index++; 821 if (ERROR_MORE_DATA == error) 822 continue; 823 if (ERROR_SUCCESS != error) 824 { 825 LastError_setLastError(error, _T(__FILE__), __LINE__); 826 break; 827 } 828 if (32 != subKeyNameLength) 829 continue; 830 error 831 = RegOpenKeyEx( 832 key, 833 subKeyName, 834 0, 835 KEY_QUERY_VALUE | KEY_WOW64_64KEY, 836 &subKey); 837 if (ERROR_SUCCESS != error) 838 { 839 LastError_setLastError(error, _T(__FILE__), __LINE__); 840 break; 841 } 842 error 843 = Run_getRegSzValue( 844 subKey, 845 _T("PackageCode"), 846 &packageCodeOfProductId); 847 if (ERROR_SUCCESS == error) 848 { 849 if (_tcsnicmp( 850 packageCodeOfProductId, packageCode, 851 subKeyNameLength) 852 == 0) 853 { 854 *productId = _tcsdup(subKeyName); 855 if (!(*productId)) 856 { 857 error = ERROR_NOT_ENOUGH_MEMORY; 858 LastError_setLastError(error, _T(__FILE__), __LINE__); 859 } 860 } 861 free(packageCodeOfProductId); 862 } 863 else 864 LastError_setLastError(error, _T(__FILE__), __LINE__); 865 RegCloseKey(subKey); 866 if (ERROR_SUCCESS != error) 867 break; 868 } 869 while (!(*productId)); 870 RegCloseKey(key); 871 if ((ERROR_SUCCESS == error) && !(*productId)) 872 { 873 error = ERROR_FILE_NOT_FOUND; 874 LastError_setLastError(error, _T(__FILE__), __LINE__); 875 } 876 } 877 else 878 LastError_setLastError(error, _T(__FILE__), __LINE__); 879 return error; 880} 881#endif /* #ifdef PACKAGECODE */ 882 883static void 884Setup_fixCommandLineQuotes() 885{ 886 LPWSTR readCmdLine = Setup_commandLine; 887 LPWSTR writeCmdLine = Setup_commandLine; 888 889 do 890 { 891 LPWSTR arg; 892 893 readCmdLine = Setup_getArgW(readCmdLine, &arg); 894 if (arg) 895 { 896 /* 897 * If the argument has unquoted whitespace, quote the whole 898 * argument. 899 */ 900 wchar_t c; 901 LPWSTR a = arg; 902 BOOL quoted = FALSE; 903 BOOL whitespace = FALSE; 904 size_t argLength; 905 906 while ((c = *a)) 907 { 908 if (iswspace(c)) 909 { 910 if (!quoted) 911 { 912 whitespace = TRUE; 913 break; 914 } 915 } 916 else if (L'\"' == c) 917 quoted = quoted ? FALSE : TRUE; 918 a++; 919 } 920 921 /* 922 * If the argument is not the first one, separate it from the 923 * previous one with a space. 924 */ 925 if (writeCmdLine != Setup_commandLine) 926 { 927 *writeCmdLine = L' '; 928 writeCmdLine++; 929 } 930 /* Append the argument to the command line. */ 931 argLength = wcslen(arg); 932 if (whitespace 933 && ((writeCmdLine + (argLength + 1)) < readCmdLine)) 934 { 935 *writeCmdLine = L'\"'; 936 writeCmdLine++; 937 wcsncpy(writeCmdLine, arg, argLength); 938 writeCmdLine += argLength; 939 *writeCmdLine = L'\"'; 940 writeCmdLine++; 941 } 942 else 943 { 944 wcsncpy(writeCmdLine, arg, argLength); 945 writeCmdLine += argLength; 946 } 947 } 948 else 949 break; 950 } 951 while (1); 952 /* At long last, terminate the command line. */ 953 *writeCmdLine = 0; 954} 955 956static LPWSTR 957Setup_getArgW(LPWSTR commandLine, LPWSTR *value) 958{ 959 if (commandLine) 960 { 961 LPWSTR v; 962 size_t quoted; 963 wchar_t prevC; 964 wchar_t c; 965 966 /* Obviously, any leading whitespace is not a part of an argument. */ 967 commandLine = Setup_skipWhitespaceW(commandLine); 968 v = commandLine; 969 970 quoted = 0; 971 prevC = 0; 972 while ((c = *commandLine)) 973 { 974 if (iswspace(c)) 975 { 976 if (!quoted) 977 break; 978 } 979 else if (L'\"' == c) 980 { 981 if (quoted) 982 { 983 /* 984 * Quotes cannot simply be nested but the Java 985 * ProcessBuilder does nest them (in cases in which nesting 986 * is not even necessary) so try to deal with them. Clearly, 987 * the implementation will be heuristic in nature. 988 */ 989 wchar_t nextC; 990 991 if ((1 == quoted) 992 && ((nextC = *(commandLine + 1))) 993 && !iswspace(nextC) 994 && ((L'=' == prevC) 995 || ((L'\"' == prevC) 996 && (v + 1 == commandLine)))) 997 quoted++; 998 else 999 quoted--; 1000 } 1001 else 1002 quoted = 1; 1003 } 1004 prevC = c; 1005 commandLine++; 1006 } 1007 /* 1008 * If there are more arguments, get rid of any whitespace before them. 1009 */ 1010 if (c) 1011 { 1012 LPWSTR vEnd = commandLine; 1013 1014 commandLine = Setup_skipWhitespaceW(commandLine); 1015 *vEnd = 0; 1016 } 1017 /* 1018 * If the argument is quoted, drop the quotes. Since the Java 1019 * ProcessBuilder will quote even the quoted command line arguments, 1020 * drop as many quotes as possible. 1021 */ 1022 while (L'\"' == *v) 1023 { 1024 size_t vLength = wcslen(v); 1025 1026 if (vLength > 1) 1027 { 1028 LPWSTR vEnd = v + (vLength - 1); 1029 1030 if (L'\"' == *vEnd) 1031 { 1032 /* 1033 * Convert the opening quote to a whitespace character (just 1034 * in case) and the closing quote to the null character (in 1035 * order to terminate the argument). 1036 */ 1037 *v = L' '; 1038 v++; 1039 *vEnd = 0; 1040 } 1041 } 1042 } 1043 1044 if (value) 1045 *value = *v ? v : NULL; 1046 } 1047 else 1048 { 1049 if (value) 1050 *value = NULL; 1051 } 1052 return commandLine; 1053} 1054 1055#define DEFINE_SETUP_GETBOOLARG(f, t, len, nicmp) \ 1056 static LP ## t \ 1057 Setup_getBoolArg ## f (LPC ## t argName, LP ## t commandLine, BOOL *boolValue) \ 1058 { \ 1059 size_t argNameLength; \ 1060 BOOL argValue; \ 1061 \ 1062 argNameLength = len(argName); \ 1063 commandLine = Setup_skipWhitespace ## f (commandLine); \ 1064 if (0 == nicmp(commandLine, argName, argNameLength)) \ 1065 { \ 1066 argValue = TRUE; \ 1067 commandLine = Setup_skipWhitespace ## f (commandLine + argNameLength); \ 1068 } \ 1069 else \ 1070 argValue = FALSE; \ 1071 if (boolValue) \ 1072 *boolValue = argValue; \ 1073 return commandLine; \ 1074 } 1075#ifdef _UNICODE 1076#undef _UNICODE 1077DEFINE_SETUP_GETBOOLARG(A, STR, strlen, _strnicmp) 1078#define _UNICODE 1079DEFINE_SETUP_GETBOOLARG(W, WSTR, wcslen, _wcsnicmp) 1080#else /* #ifdef _UNICODE */ 1081DEFINE_SETUP_GETBOOLARG(A, STR, strlen, _strnicmp) 1082#define _UNICODE 1083DEFINE_SETUP_GETBOOLARG(W, WSTR, wcslen, _wcsnicmp) 1084#undef _UNICODE 1085#endif /* #ifdef _UNICODE */ 1086 1087static LPCTSTR 1088Setup_getFileName() 1089{ 1090 if (!Setup_fileName) 1091 { 1092 TCHAR moduleFileName[MAX_PATH + 1]; 1093 DWORD moduleFileNameSize = sizeof(moduleFileName) / sizeof(TCHAR); 1094 DWORD moduleFileNameLength 1095 = GetModuleFileName(NULL, moduleFileName, moduleFileNameSize); 1096 1097 if (moduleFileNameLength) 1098 { 1099 TCHAR *fileNameEnd = moduleFileName + moduleFileNameLength - 1; 1100 TCHAR *fileNameBegin = fileNameEnd; 1101 size_t fileNameLength; 1102 LPTSTR fileName; 1103 1104 for (; fileNameBegin >= moduleFileName; fileNameBegin--) 1105 { 1106 TCHAR c = *fileNameBegin; 1107 1108 if ((_T('\\') == c) || (_T('/') == c)) 1109 break; 1110 } 1111 fileNameBegin 1112 = (fileNameBegin == fileNameEnd) 1113 ? moduleFileName 1114 : (fileNameBegin + 1); 1115 1116 fileNameLength = (fileNameEnd - fileNameBegin) + 1; 1117 fileName = (LPTSTR) malloc((fileNameLength + 1) * sizeof(TCHAR)); 1118 if (fileName) 1119 { 1120 _tcsncpy(fileName, fileNameBegin, fileNameLength); 1121 *(fileName + fileNameLength) = 0; 1122 Setup_fileName = fileName; 1123 } 1124 } 1125 } 1126 return Setup_fileName; 1127} 1128 1129static DWORD 1130Setup_getParentProcess(DWORD *ppid, LPTSTR *fileName) 1131{ 1132 HANDLE snapshot; 1133 DWORD error; 1134 1135 snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 1136 if (snapshot == INVALID_HANDLE_VALUE) 1137 { 1138 error = GetLastError(); 1139 LastError_setLastError(error, _T(__FILE__), __LINE__); 1140 } 1141 else 1142 { 1143 PROCESSENTRY32 entry; 1144 1145 entry.dwSize = sizeof(PROCESSENTRY32); 1146 if (Process32First(snapshot, &entry)) 1147 { 1148 DWORD pid; 1149 1150 error = ERROR_SUCCESS; 1151 pid = GetCurrentProcessId(); 1152 if (ppid) 1153 *ppid = 0; 1154 1155 do 1156 { 1157 if (entry.th32ProcessID == pid) 1158 { 1159 if (ppid) 1160 *ppid = entry.th32ParentProcessID; 1161 break; 1162 } 1163 if (!Process32Next(snapshot, &entry)) 1164 { 1165 error = GetLastError(); 1166 LastError_setLastError(error, _T(__FILE__), __LINE__); 1167 break; 1168 } 1169 } 1170 while (1); 1171 } 1172 else 1173 { 1174 error = GetLastError(); 1175 LastError_setLastError(error, _T(__FILE__), __LINE__); 1176 } 1177 if ((ERROR_SUCCESS == error) && fileName && ppid && *ppid) 1178 { 1179 if (Process32First(snapshot, &entry)) 1180 { 1181 do 1182 { 1183 if (entry.th32ProcessID == *ppid) 1184 { 1185 *fileName = _tcsdup(entry.szExeFile); 1186 if (NULL == *fileName) 1187 { 1188 error = ERROR_OUTOFMEMORY; 1189 LastError_setLastError(error, _T(__FILE__), __LINE__); 1190 } 1191 break; 1192 } 1193 if (!Process32Next(snapshot, &entry)) 1194 { 1195 error = GetLastError(); 1196 LastError_setLastError(error, _T(__FILE__), __LINE__); 1197 break; 1198 } 1199 } 1200 while (1); 1201 } 1202 else 1203 { 1204 error = GetLastError(); 1205 LastError_setLastError(error, _T(__FILE__), __LINE__); 1206 } 1207 } 1208 CloseHandle(snapshot); 1209 } 1210 return error; 1211} 1212 1213static LPCTSTR 1214Setup_getProductName() 1215{ 1216 if (!Setup_productName) 1217 { 1218 LPCTSTR fileName = Setup_getFileName(); 1219 1220 if (fileName) 1221 { 1222 int fileNameLength = _tcslen(fileName); 1223 1224 if ((fileNameLength > 4) 1225 && (_tcsnicmp(fileName + fileNameLength - 4, _T(".exe"), 4) 1226 == 0)) 1227 { 1228 LPTSTR productName; 1229 1230 fileNameLength -= 4; 1231 productName 1232 = (LPTSTR) malloc((fileNameLength + 1) * sizeof(TCHAR)); 1233 if (productName) 1234 { 1235 _tcsncpy(productName, fileName, fileNameLength); 1236 *(productName + fileNameLength) = 0; 1237 Setup_productName = productName; 1238 } 1239 } 1240 if (!Setup_productName) 1241 Setup_productName = (LPTSTR) fileName; 1242 } 1243 } 1244 return Setup_productName; 1245} 1246 1247static DWORD 1248Setup_getWinMainCmdLine(LPTSTR *winMainCmdLine) 1249{ 1250 LPWSTR cmdLineW = GetCommandLineW(); 1251 DWORD error; 1252 1253 if (cmdLineW && wcslen(cmdLineW)) 1254 { 1255 int argc; 1256 LPWSTR *argvW = CommandLineToArgvW(cmdLineW, &argc); 1257 1258 if (argvW) 1259 { 1260 if (argc) 1261 { 1262 LPWSTR argvW0 = argvW[0]; 1263 LPWSTR argvW0InCmdLineW = wcsstr(cmdLineW, argvW0); 1264 1265 if (argvW0InCmdLineW) 1266 { 1267 wchar_t c; 1268 1269 cmdLineW = argvW0InCmdLineW + wcslen(argvW0); 1270 /* 1271 * CommandLineToArgvW may not report quotes as part of 1272 * argvW0. As a workaround, skip non-whitespace characters. 1273 */ 1274 while ((c = *cmdLineW) && !iswspace(c)) 1275 cmdLineW++; 1276 } 1277#ifdef _UNICODE 1278 *winMainCmdLine = cmdLineW; 1279 error = ERROR_SUCCESS; 1280#else /* #ifdef _UNICODE */ 1281 *winMainCmdLine = NLS_wstr2str(cmdLineW); 1282 if (*winMainCmdLine) 1283 error = ERROR_SUCCESS; 1284 else 1285 { 1286 error = ERROR_GEN_FAILURE; 1287 LastError_setLastError(error, _T(__FILE__), __LINE__); 1288 } 1289#endif /* #ifdef _UNICODE */ 1290 } 1291 else 1292 { 1293 *winMainCmdLine = NULL; 1294 error = ERROR_SUCCESS; 1295 } 1296 LocalFree(argvW); 1297 } 1298 else 1299 { 1300 error = GetLastError(); 1301 LastError_setLastError(error, _T(__FILE__), __LINE__); 1302 } 1303 } 1304 else 1305 { 1306 *winMainCmdLine = NULL; 1307 error = ERROR_SUCCESS; 1308 } 1309 return error; 1310} 1311 1312static int 1313Setup_isWow64Acceptable() 1314{ 1315 int answer = IDYES; 1316 HMODULE kernel32; 1317 1318 /* 1319 * If this is an (automatic) update, do not ask because (1) the user has 1320 * already answered during the initial install and (2) it is plain annoying. 1321 */ 1322 if (Setup_commandLine 1323 && wcsstr( 1324 Setup_commandLine, 1325 SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR_PROPERTY_BEGIN)) 1326 return answer; 1327 1328 kernel32 = GetModuleHandle(_T("kernel32")); 1329 if (kernel32) 1330 { 1331 typedef BOOL (WINAPI *LPISWOW64PROCESS)(HANDLE, PBOOL); 1332 1333 LPISWOW64PROCESS isWow64Process 1334 = (LPISWOW64PROCESS) GetProcAddress(kernel32, "IsWow64Process"); 1335 BOOL wow64Process = FALSE; 1336 1337 if (isWow64Process 1338 && isWow64Process(GetCurrentProcess(), &wow64Process) 1339 && wow64Process) 1340 { 1341 TCHAR fileName[MAX_PATH + 1]; 1342 1343 if (GetModuleFileName(NULL, fileName, sizeof(fileName) / sizeof(TCHAR))) 1344 { 1345 UINT questionId; 1346 UINT buttonType; 1347 DWORD questionLength; 1348 TCHAR question[1024]; 1349 1350#ifdef X64_SETUP_URL 1351 HHOOK hook 1352 = SetWindowsHookEx( 1353 WH_CALLWNDPROCRET, 1354 (HOOKPROC) Setup_isWow64AcceptableMessageBoxCallWndRetProc, 1355 NULL, 1356 GetCurrentThreadId()); 1357 1358 if (hook) 1359 { 1360 questionId = IDS_ISWOW64ACCEPTABLE3; 1361 buttonType = MB_YESNOCANCEL | MB_DEFBUTTON3; 1362 } 1363 else 1364#endif /* #ifdef X64_SETUP_URL */ 1365 { 1366 questionId = IDS_ISWOW64ACCEPTABLE2; 1367 buttonType = MB_YESNO; 1368 } 1369 1370 questionLength 1371 = LoadString( 1372 GetModuleHandle(NULL), 1373 questionId, 1374 question, 1375 sizeof(question) / sizeof(TCHAR)); 1376 if (questionLength) 1377 { 1378 answer 1379 = MessageBox( 1380 NULL, 1381 question, 1382 fileName, 1383 MB_ICONQUESTION | buttonType); 1384 LocalFree(question); 1385 } 1386 1387#ifdef X64_SETUP_URL 1388 if (hook) 1389 { 1390 UnhookWindowsHookEx(hook); 1391 1392 switch (answer) 1393 { 1394 case IDNO: // Continue 1395 answer = IDYES; 1396 break; 1397 case IDYES: // Download 1398 answer = IDNO; 1399 ShellExecute( 1400 NULL, 1401 _T("open"), 1402 _T(X64_SETUP_URL), 1403 NULL, 1404 NULL, 1405 SW_SHOWNORMAL); 1406 break; 1407 } 1408 } 1409#endif /* #ifdef X64_SETUP_URL */ 1410 } 1411 } 1412 } 1413 return answer; 1414} 1415 1416LRESULT CALLBACK 1417Setup_isWow64AcceptableMessageBoxCallWndRetProc( 1418 int code, 1419 WPARAM wParam, 1420 LPARAM lParam) 1421{ 1422 CWPRETSTRUCT *cwprs = (CWPRETSTRUCT *) lParam; 1423 1424 if (cwprs && (WM_INITDIALOG == cwprs->message)) 1425 { 1426 HWND yes, no; 1427 1428 yes = GetDlgItem(cwprs->hwnd, IDYES); 1429 if (yes) 1430 SendMessage(yes, WM_SETTEXT, 0, (LPARAM) _T("&Download")); 1431 1432 no = GetDlgItem(cwprs->hwnd, IDNO); 1433 if (no) 1434 SendMessage(no, WM_SETTEXT, 0, (LPARAM) _T("&Continue")); 1435 } 1436 return CallNextHookEx(NULL, code, wParam, lParam); 1437} 1438 1439static DWORD 1440Setup_msiexec() 1441{ 1442 LPWSTR msi; 1443 DWORD error; 1444 1445 Setup_commandLine = Setup_getArgW(Setup_commandLine, &msi); 1446 if (msi) 1447 error = Setup_executeMsiW(msi); 1448 else 1449 { 1450 error = ERROR_BAD_ARGUMENTS; 1451 LastError_setLastError(error, _T(__FILE__), __LINE__); 1452 } 1453 return error; 1454} 1455 1456static DWORD 1457Setup_parseCommandLine(LPTSTR cmdLine) 1458{ 1459 LPTSTR commandLine; 1460 DWORD error = ERROR_SUCCESS; 1461 1462//#ifdef _UNICODE 1463// if (cmdLine) 1464// { 1465// commandLine = NLS_str2wstr(cmdLine); 1466// if (!commandLine) 1467// { 1468// error = ERROR_OUTOFMEMORY; 1469// LastError_setLastError(error, _T(__FILE__), __LINE__); 1470// } 1471// } 1472// else 1473// commandLine = NULL; 1474//#else 1475 commandLine = cmdLine; 1476//#endif /* #ifdef _UNICODE */ 1477 1478 if (commandLine) 1479 { 1480 LPTSTR noWaitParentCommandLine 1481 = Setup_getBoolArg( 1482 _T("--wait-parent"), 1483 commandLine, 1484 &Setup_waitForParentProcess_); 1485 /* 1486 * The command line argument --allow-elevation is up2date legacy which 1487 * has to be taken into account by removing it in order to prevent it 1488 * from breaking msiexec. 1489 */ 1490 BOOL up2date; 1491 LPTSTR noAllowElevationCommandLine 1492 = Setup_getBoolArg( 1493 _T("--allow-elevation"), 1494 noWaitParentCommandLine, 1495 &up2date); 1496 size_t noAllowElevationCommandLineLength 1497 = _tcslen(noAllowElevationCommandLine); 1498 TCHAR envVarValue[1 /* " */ + MAX_PATH + 1 /* " */ + 1]; 1499 1500 /* 1501 * If there are no arguments on the command line to reveal that up2date 1502 * is involved, try detecting it by the fact that it sets the 1503 * SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR environment variable. If the 1504 * environment variable in question is not set, it is sure that this 1505 * setup is to execute its post-up2date logic. 1506 */ 1507 if (!up2date && !noAllowElevationCommandLineLength) 1508 { 1509 DWORD envVarValueSize 1510 = (sizeof(envVarValue) / sizeof(TCHAR)) - 2 /* "" */; 1511 DWORD envVarValueLength 1512 = GetEnvironmentVariable( 1513 _T("SIP_COMMUNICATOR_AUTOUPDATE_INSTALLDIR"), 1514 &(envVarValue[1]), 1515 envVarValueSize); 1516 1517 if (envVarValueLength) 1518 { 1519 if (envVarValueLength > envVarValueSize) 1520 { 1521 error = ERROR_NOT_ENOUGH_MEMORY; 1522 LastError_…
Large files files are truncated, but you can click here to view the full file