1 /* 2 * Custom Action processing for the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2005 Aric Stewart for CodeWeavers 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #define COBJMACROS 22 23 #include <stdarg.h> 24 #include <stdio.h> 25 26 #include "ntstatus.h" 27 #define WIN32_NO_STATUS 28 #include "windef.h" 29 #include "winbase.h" 30 #include "winerror.h" 31 #include "msidefs.h" 32 #include "winuser.h" 33 #include "objbase.h" 34 #include "oleauto.h" 35 36 #include "msipriv.h" 37 #include "winemsi_s.h" 38 #include "wine/asm.h" 39 #include "wine/debug.h" 40 #include "wine/unicode.h" 41 #include "wine/exception.h" 42 43 #ifdef __REACTOS__ 44 #undef WIN32_NO_STATUS 45 #include <psdk/ntstatus.h> 46 #include <ndk/mmfuncs.h> 47 #endif 48 49 WINE_DEFAULT_DEBUG_CHANNEL(msi); 50 51 #define CUSTOM_ACTION_TYPE_MASK 0x3F 52 53 struct running_action 54 { 55 struct list entry; 56 HANDLE handle; 57 BOOL process; 58 LPWSTR name; 59 }; 60 61 typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE ); 62 63 static CRITICAL_SECTION custom_action_cs; 64 static CRITICAL_SECTION_DEBUG custom_action_cs_debug = 65 { 66 0, 0, &custom_action_cs, 67 { &custom_action_cs_debug.ProcessLocksList, 68 &custom_action_cs_debug.ProcessLocksList }, 69 0, 0, { (DWORD_PTR)(__FILE__ ": custom_action_cs") } 70 }; 71 static CRITICAL_SECTION custom_action_cs = { &custom_action_cs_debug, -1, 0, 0, 0, 0 }; 72 73 static struct list pending_custom_actions = LIST_INIT( pending_custom_actions ); 74 75 void __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len) 76 { 77 return malloc(len); 78 } 79 80 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr) 81 { 82 free(ptr); 83 } 84 85 LONG WINAPI rpc_filter(EXCEPTION_POINTERS *eptr) 86 { 87 return I_RpcExceptionFilter(eptr->ExceptionRecord->ExceptionCode); 88 } 89 90 UINT msi_schedule_action( MSIPACKAGE *package, UINT script, const WCHAR *action ) 91 { 92 UINT count; 93 WCHAR **newbuf = NULL; 94 95 if (script >= SCRIPT_MAX) 96 { 97 FIXME("Unknown script requested %u\n", script); 98 return ERROR_FUNCTION_FAILED; 99 } 100 TRACE("Scheduling action %s in script %u\n", debugstr_w(action), script); 101 102 count = package->script_actions_count[script]; 103 package->script_actions_count[script]++; 104 if (count != 0) newbuf = realloc( package->script_actions[script], 105 package->script_actions_count[script] * sizeof(WCHAR *) ); 106 else newbuf = malloc( sizeof(WCHAR *) ); 107 108 newbuf[count] = wcsdup( action ); 109 package->script_actions[script] = newbuf; 110 return ERROR_SUCCESS; 111 } 112 113 UINT msi_register_unique_action( MSIPACKAGE *package, const WCHAR *action ) 114 { 115 UINT count; 116 WCHAR **newbuf = NULL; 117 118 TRACE("Registering %s as unique action\n", debugstr_w(action)); 119 120 count = package->unique_actions_count; 121 package->unique_actions_count++; 122 if (count != 0) newbuf = realloc( package->unique_actions, 123 package->unique_actions_count * sizeof(WCHAR *) ); 124 else newbuf = malloc( sizeof(WCHAR *) ); 125 126 newbuf[count] = wcsdup( action ); 127 package->unique_actions = newbuf; 128 return ERROR_SUCCESS; 129 } 130 131 BOOL msi_action_is_unique( const MSIPACKAGE *package, const WCHAR *action ) 132 { 133 UINT i; 134 135 for (i = 0; i < package->unique_actions_count; i++) 136 { 137 if (!wcscmp( package->unique_actions[i], action )) return TRUE; 138 } 139 return FALSE; 140 } 141 142 static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options) 143 { 144 if ((options & msidbCustomActionTypeClientRepeat) == 145 msidbCustomActionTypeClientRepeat) 146 { 147 if (!(package->InWhatSequence & SEQUENCE_UI && 148 package->InWhatSequence & SEQUENCE_EXEC)) 149 { 150 TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n"); 151 return FALSE; 152 } 153 } 154 else if (options & msidbCustomActionTypeFirstSequence) 155 { 156 if (package->InWhatSequence & SEQUENCE_UI && 157 package->InWhatSequence & SEQUENCE_EXEC ) 158 { 159 TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n"); 160 return FALSE; 161 } 162 } 163 else if (options & msidbCustomActionTypeOncePerProcess) 164 { 165 if (msi_action_is_unique(package, action)) 166 { 167 TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n"); 168 return FALSE; 169 } 170 else 171 msi_register_unique_action(package, action); 172 } 173 174 return TRUE; 175 } 176 177 /* stores the following properties before the action: 178 * 179 * [CustomActionData<=>UserSID<=>ProductCode]Action 180 */ 181 static WCHAR *get_deferred_action(const WCHAR *action, const WCHAR *actiondata, const WCHAR *usersid, 182 const WCHAR *prodcode) 183 { 184 LPWSTR deferred; 185 DWORD len; 186 187 if (!actiondata) 188 return wcsdup(action); 189 190 len = lstrlenW(action) + lstrlenW(actiondata) + 191 lstrlenW(usersid) + lstrlenW(prodcode) + 192 lstrlenW(L"[%s<=>%s<=>%s]%s") - 7; 193 deferred = malloc(len * sizeof(WCHAR)); 194 195 swprintf(deferred, len, L"[%s<=>%s<=>%s]%s", actiondata, usersid, prodcode, action); 196 return deferred; 197 } 198 199 static void set_deferred_action_props( MSIPACKAGE *package, const WCHAR *deferred_data ) 200 { 201 const WCHAR *end, *beg = deferred_data + 1; 202 203 end = wcsstr(beg, L"<=>"); 204 msi_set_property( package->db, L"CustomActionData", beg, end - beg ); 205 beg = end + 3; 206 207 end = wcsstr(beg, L"<=>"); 208 msi_set_property( package->db, L"UserSID", beg, end - beg ); 209 beg = end + 3; 210 211 end = wcschr(beg, ']'); 212 msi_set_property( package->db, L"ProductCode", beg, end - beg ); 213 } 214 215 WCHAR *msi_create_temp_file( MSIDATABASE *db ) 216 { 217 WCHAR *ret; 218 219 if (!db->tempfolder) 220 { 221 WCHAR tmp[MAX_PATH]; 222 DWORD len = ARRAY_SIZE( tmp ); 223 224 if (msi_get_property( db, L"TempFolder", tmp, &len ) || 225 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY) 226 { 227 GetTempPathW( MAX_PATH, tmp ); 228 } 229 if (!(db->tempfolder = wcsdup( tmp ))) return NULL; 230 } 231 232 if ((ret = malloc( (wcslen( db->tempfolder ) + 20) * sizeof(WCHAR) ))) 233 { 234 if (!GetTempFileNameW( db->tempfolder, L"msi", 0, ret )) 235 { 236 free( ret ); 237 return NULL; 238 } 239 } 240 241 return ret; 242 } 243 244 static MSIBINARY *create_temp_binary(MSIPACKAGE *package, LPCWSTR source) 245 { 246 MSIRECORD *row; 247 MSIBINARY *binary = NULL; 248 HANDLE file; 249 CHAR buffer[1024]; 250 WCHAR *tmpfile; 251 DWORD sz, write; 252 UINT r; 253 254 if (!(tmpfile = msi_create_temp_file( package->db ))) return NULL; 255 256 if (!(row = MSI_QueryGetRecord( package->db, L"SELECT * FROM `Binary` WHERE `Name` = '%s'", source ))) goto error; 257 if (!(binary = calloc( 1, sizeof(MSIBINARY) ))) goto error; 258 259 file = CreateFileW( tmpfile, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); 260 if (file == INVALID_HANDLE_VALUE) goto error; 261 262 do 263 { 264 sz = sizeof(buffer); 265 r = MSI_RecordReadStream( row, 2, buffer, &sz ); 266 if (r != ERROR_SUCCESS) 267 { 268 ERR("Failed to get stream\n"); 269 break; 270 } 271 WriteFile( file, buffer, sz, &write, NULL ); 272 } while (sz == sizeof buffer); 273 274 CloseHandle( file ); 275 if (r != ERROR_SUCCESS) goto error; 276 277 binary->source = wcsdup( source ); 278 binary->tmpfile = tmpfile; 279 list_add_tail( &package->binaries, &binary->entry ); 280 281 msiobj_release( &row->hdr ); 282 return binary; 283 284 error: 285 if (row) msiobj_release( &row->hdr ); 286 DeleteFileW( tmpfile ); 287 free( tmpfile ); 288 free( binary ); 289 return NULL; 290 } 291 292 static MSIBINARY *get_temp_binary(MSIPACKAGE *package, LPCWSTR source) 293 { 294 MSIBINARY *binary; 295 296 LIST_FOR_EACH_ENTRY( binary, &package->binaries, MSIBINARY, entry ) 297 { 298 if (!wcscmp( binary->source, source )) 299 return binary; 300 } 301 302 return create_temp_binary(package, source); 303 } 304 305 static void file_running_action(MSIPACKAGE* package, HANDLE Handle, 306 BOOL process, LPCWSTR name) 307 { 308 struct running_action *action; 309 310 action = malloc( sizeof(*action) ); 311 312 action->handle = Handle; 313 action->process = process; 314 action->name = wcsdup(name); 315 316 list_add_tail( &package->RunningActions, &action->entry ); 317 } 318 319 static UINT custom_get_process_return( HANDLE process ) 320 { 321 DWORD rc = 0; 322 323 GetExitCodeProcess( process, &rc ); 324 TRACE( "exit code is %lu\n", rc ); 325 if (rc != 0) 326 return ERROR_FUNCTION_FAILED; 327 return ERROR_SUCCESS; 328 } 329 330 static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread ) 331 { 332 DWORD rc = 0; 333 334 GetExitCodeThread( thread, &rc ); 335 336 switch (rc) 337 { 338 case ERROR_FUNCTION_NOT_CALLED: 339 case ERROR_SUCCESS: 340 case ERROR_INSTALL_USEREXIT: 341 case ERROR_INSTALL_FAILURE: 342 return rc; 343 case ERROR_NO_MORE_ITEMS: 344 return ERROR_SUCCESS; 345 case ERROR_INSTALL_SUSPEND: 346 ACTION_ForceReboot( package ); 347 return ERROR_SUCCESS; 348 default: 349 ERR( "invalid Return Code %lu\n", rc ); 350 return ERROR_INSTALL_FAILURE; 351 } 352 } 353 354 static UINT wait_process_handle(MSIPACKAGE* package, UINT type, 355 HANDLE ProcessHandle, LPCWSTR name) 356 { 357 UINT rc = ERROR_SUCCESS; 358 359 if (!(type & msidbCustomActionTypeAsync)) 360 { 361 TRACE("waiting for %s\n", debugstr_w(name)); 362 363 msi_dialog_check_messages(ProcessHandle); 364 365 if (!(type & msidbCustomActionTypeContinue)) 366 rc = custom_get_process_return(ProcessHandle); 367 368 CloseHandle(ProcessHandle); 369 } 370 else 371 { 372 TRACE("%s running in background\n", debugstr_w(name)); 373 374 if (!(type & msidbCustomActionTypeContinue)) 375 file_running_action(package, ProcessHandle, TRUE, name); 376 else 377 CloseHandle(ProcessHandle); 378 } 379 380 return rc; 381 } 382 383 typedef struct 384 { 385 struct list entry; 386 MSIPACKAGE *package; 387 LPWSTR source; 388 LPWSTR target; 389 HANDLE handle; 390 LPWSTR action; 391 INT type; 392 GUID guid; 393 DWORD arch; 394 } custom_action_info; 395 396 static void free_custom_action_data( custom_action_info *info ) 397 { 398 EnterCriticalSection( &custom_action_cs ); 399 400 list_remove( &info->entry ); 401 if (info->handle) 402 CloseHandle( info->handle ); 403 free( info->action ); 404 free( info->source ); 405 free( info->target ); 406 msiobj_release( &info->package->hdr ); 407 free( info ); 408 409 LeaveCriticalSection( &custom_action_cs ); 410 } 411 412 static UINT wait_thread_handle( custom_action_info *info ) 413 { 414 UINT rc = ERROR_SUCCESS; 415 416 if (!(info->type & msidbCustomActionTypeAsync)) 417 { 418 TRACE("waiting for %s\n", debugstr_w( info->action )); 419 420 msi_dialog_check_messages( info->handle ); 421 422 if (!(info->type & msidbCustomActionTypeContinue)) 423 rc = custom_get_thread_return( info->package, info->handle ); 424 425 free_custom_action_data( info ); 426 } 427 else 428 { 429 TRACE("%s running in background\n", debugstr_w( info->action )); 430 } 431 432 return rc; 433 } 434 435 static custom_action_info *find_action_by_guid( const GUID *guid ) 436 { 437 custom_action_info *info; 438 BOOL found = FALSE; 439 440 EnterCriticalSection( &custom_action_cs ); 441 442 LIST_FOR_EACH_ENTRY( info, &pending_custom_actions, custom_action_info, entry ) 443 { 444 if (IsEqualGUID( &info->guid, guid )) 445 { 446 found = TRUE; 447 break; 448 } 449 } 450 451 LeaveCriticalSection( &custom_action_cs ); 452 453 if (!found) 454 return NULL; 455 456 return info; 457 } 458 459 static void handle_msi_break( const WCHAR *action ) 460 { 461 const WCHAR fmt[] = L"To debug your custom action, attach your debugger to process %u (0x%x) and press OK"; 462 WCHAR val[MAX_PATH], msg[100]; 463 464 if (!GetEnvironmentVariableW( L"MsiBreak", val, MAX_PATH ) || wcscmp( val, action )) return; 465 466 swprintf( msg, ARRAY_SIZE(msg), fmt, GetCurrentProcessId(), GetCurrentProcessId() ); 467 MessageBoxW( NULL, msg, L"Windows Installer", MB_OK ); 468 DebugBreak(); 469 } 470 471 #if defined __i386__ && defined _MSC_VER 472 __declspec(naked) UINT custom_proc_wrapper(MsiCustomActionEntryPoint entry, MSIHANDLE hinst) 473 { 474 __asm 475 { 476 push ebp 477 mov ebp, esp 478 sub esp, 4 479 push [ebp+12] 480 call [ebp+8] 481 leave 482 ret 483 } 484 } 485 #elif defined __i386__ && defined __GNUC__ 486 /* wrapper for apps that don't declare the thread function correctly */ 487 extern UINT custom_proc_wrapper( MsiCustomActionEntryPoint entry, MSIHANDLE hinst ); 488 __ASM_GLOBAL_FUNC(custom_proc_wrapper, 489 "pushl %ebp\n\t" 490 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") 491 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") 492 "movl %esp,%ebp\n\t" 493 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") 494 "subl $4,%esp\n\t" 495 "pushl 12(%ebp)\n\t" 496 "call *8(%ebp)\n\t" 497 "leave\n\t" 498 __ASM_CFI(".cfi_def_cfa %esp,4\n\t") 499 __ASM_CFI(".cfi_same_value %ebp\n\t") 500 "ret" ) 501 #else 502 static UINT custom_proc_wrapper( MsiCustomActionEntryPoint entry, MSIHANDLE hinst ) 503 { 504 return entry(hinst); 505 } 506 #endif 507 508 UINT CDECL __wine_msi_call_dll_function(DWORD client_pid, const GUID *guid) 509 { 510 MsiCustomActionEntryPoint fn; 511 MSIHANDLE remote_package = 0; 512 RPC_WSTR binding_str; 513 MSIHANDLE hPackage; 514 RPC_STATUS status; 515 WCHAR *dll = NULL, *action = NULL; 516 LPSTR proc = NULL; 517 HANDLE hModule; 518 INT type; 519 UINT r; 520 521 TRACE("%s\n", debugstr_guid( guid )); 522 523 if (!rpc_handle) 524 { 525 WCHAR endpoint[12]; 526 527 swprintf(endpoint, ARRAY_SIZE(endpoint), L"msi%x", client_pid); 528 status = RpcStringBindingComposeW(NULL, (WCHAR *)L"ncalrpc", NULL, endpoint, NULL, &binding_str); 529 if (status != RPC_S_OK) 530 { 531 ERR("RpcStringBindingCompose failed: %#lx\n", status); 532 return status; 533 } 534 status = RpcBindingFromStringBindingW(binding_str, &rpc_handle); 535 if (status != RPC_S_OK) 536 { 537 ERR("RpcBindingFromStringBinding failed: %#lx\n", status); 538 return status; 539 } 540 RpcStringFreeW(&binding_str); 541 } 542 543 r = remote_GetActionInfo(guid, &action, &type, &dll, &proc, &remote_package); 544 if (r != ERROR_SUCCESS) 545 return r; 546 547 hPackage = alloc_msi_remote_handle( remote_package ); 548 if (!hPackage) 549 { 550 ERR( "failed to create handle for %#lx\n", remote_package ); 551 midl_user_free( action ); 552 midl_user_free( dll ); 553 midl_user_free( proc ); 554 return ERROR_INSTALL_FAILURE; 555 } 556 557 hModule = LoadLibraryW( dll ); 558 if (!hModule) 559 { 560 ERR( "failed to load dll %s (%lu)\n", debugstr_w( dll ), GetLastError() ); 561 midl_user_free( action ); 562 midl_user_free( dll ); 563 midl_user_free( proc ); 564 MsiCloseHandle( hPackage ); 565 return ERROR_SUCCESS; 566 } 567 568 fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc ); 569 if (!fn) WARN( "GetProcAddress(%s) failed\n", debugstr_a(proc) ); 570 else 571 { 572 handle_msi_break(action); 573 574 __TRY 575 { 576 r = custom_proc_wrapper( fn, hPackage ); 577 } 578 __EXCEPT_PAGE_FAULT 579 { 580 ERR( "Custom action (%s:%s) caused a page fault: %#lx\n", 581 debugstr_w(dll), debugstr_a(proc), GetExceptionCode() ); 582 r = ERROR_SUCCESS; 583 } 584 __ENDTRY; 585 } 586 587 FreeLibrary(hModule); 588 589 midl_user_free(action); 590 midl_user_free(dll); 591 midl_user_free(proc); 592 593 MsiCloseAllHandles(); 594 return r; 595 } 596 597 static HANDLE get_admin_token(void) 598 { 599 TOKEN_ELEVATION_TYPE type; 600 TOKEN_LINKED_TOKEN linked; 601 DWORD size; 602 603 #ifdef __REACTOS__ 604 #ifndef GetCurrentThreadEffectiveToken 605 #define GetCurrentProcessToken() ((HANDLE)~(ULONG_PTR)3) 606 #define GetCurrentThreadEffectiveToken() GetCurrentProcessToken() 607 #endif 608 #endif 609 610 if (!GetTokenInformation(GetCurrentThreadEffectiveToken(), TokenElevationType, &type, sizeof(type), &size) 611 || type == TokenElevationTypeFull) 612 return NULL; 613 614 if (!GetTokenInformation(GetCurrentThreadEffectiveToken(), TokenLinkedToken, &linked, sizeof(linked), &size)) 615 return NULL; 616 return linked.LinkedToken; 617 } 618 619 static DWORD custom_start_server(MSIPACKAGE *package, DWORD arch) 620 { 621 WCHAR path[MAX_PATH], cmdline[MAX_PATH + 23]; 622 PROCESS_INFORMATION pi = {0}; 623 STARTUPINFOW si = {0}; 624 WCHAR buffer[24]; 625 HANDLE token; 626 void *cookie; 627 HANDLE pipe; 628 629 if ((arch == SCS_32BIT_BINARY && package->custom_server_32_process) || 630 (arch == SCS_64BIT_BINARY && package->custom_server_64_process)) 631 return ERROR_SUCCESS; 632 633 swprintf(buffer, ARRAY_SIZE(buffer), L"\\\\.\\pipe\\msica_%x_%d", 634 GetCurrentProcessId(), arch == SCS_32BIT_BINARY ? 32 : 64); 635 pipe = CreateNamedPipeW(buffer, PIPE_ACCESS_DUPLEX, 0, 1, sizeof(DWORD64), 636 sizeof(GUID), 0, NULL); 637 if (pipe == INVALID_HANDLE_VALUE) 638 ERR("failed to create custom action client pipe: %lu\n", GetLastError()); 639 640 if ((sizeof(void *) == 8 || is_wow64) && arch == SCS_32BIT_BINARY) 641 GetSystemWow64DirectoryW(path, MAX_PATH - ARRAY_SIZE(L"\\msiexec.exe")); 642 else 643 GetSystemDirectoryW(path, MAX_PATH - ARRAY_SIZE(L"\\msiexec.exe")); 644 lstrcatW(path, L"\\msiexec.exe"); 645 swprintf(cmdline, ARRAY_SIZE(cmdline), L"%s -Embedding %d", path, GetCurrentProcessId()); 646 647 token = get_admin_token(); 648 649 if (is_wow64 && arch == SCS_64BIT_BINARY) 650 { 651 Wow64DisableWow64FsRedirection(&cookie); 652 CreateProcessAsUserW(token, path, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 653 Wow64RevertWow64FsRedirection(cookie); 654 } 655 else 656 CreateProcessAsUserW(token, path, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 657 658 if (token) CloseHandle(token); 659 660 CloseHandle(pi.hThread); 661 662 if (arch == SCS_32BIT_BINARY) 663 { 664 package->custom_server_32_process = pi.hProcess; 665 package->custom_server_32_pipe = pipe; 666 } 667 else 668 { 669 package->custom_server_64_process = pi.hProcess; 670 package->custom_server_64_pipe = pipe; 671 } 672 673 if (!ConnectNamedPipe(pipe, NULL)) 674 { 675 ERR("failed to connect to custom action server: %lu\n", GetLastError()); 676 return GetLastError(); 677 } 678 679 return ERROR_SUCCESS; 680 } 681 682 void custom_stop_server(HANDLE process, HANDLE pipe) 683 { 684 DWORD size; 685 686 WriteFile(pipe, &GUID_NULL, sizeof(GUID_NULL), &size, NULL); 687 WaitForSingleObject(process, INFINITE); 688 CloseHandle(process); 689 CloseHandle(pipe); 690 } 691 692 static DWORD WINAPI custom_client_thread(void *arg) 693 { 694 custom_action_info *info = arg; 695 DWORD64 thread64; 696 HANDLE process; 697 HANDLE thread; 698 HANDLE pipe; 699 DWORD size; 700 DWORD rc; 701 702 CoInitializeEx(NULL, COINIT_MULTITHREADED); /* needed to marshal streams */ 703 704 if (info->arch == SCS_32BIT_BINARY) 705 { 706 process = info->package->custom_server_32_process; 707 pipe = info->package->custom_server_32_pipe; 708 } 709 else 710 { 711 process = info->package->custom_server_64_process; 712 pipe = info->package->custom_server_64_pipe; 713 } 714 715 EnterCriticalSection(&custom_action_cs); 716 717 if (!WriteFile(pipe, &info->guid, sizeof(info->guid), &size, NULL) || 718 size != sizeof(info->guid)) 719 { 720 ERR("failed to write to custom action client pipe: %lu\n", GetLastError()); 721 LeaveCriticalSection(&custom_action_cs); 722 return GetLastError(); 723 } 724 if (!ReadFile(pipe, &thread64, sizeof(thread64), &size, NULL) || size != sizeof(thread64)) 725 { 726 ERR("failed to read from custom action client pipe: %lu\n", GetLastError()); 727 LeaveCriticalSection(&custom_action_cs); 728 return GetLastError(); 729 } 730 731 LeaveCriticalSection(&custom_action_cs); 732 733 if (DuplicateHandle(process, (HANDLE)(DWORD_PTR)thread64, GetCurrentProcess(), 734 &thread, 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) 735 { 736 WaitForSingleObject(thread, INFINITE); 737 GetExitCodeThread(thread, &rc); 738 CloseHandle(thread); 739 } 740 else 741 rc = GetLastError(); 742 743 CoUninitialize(); 744 return rc; 745 } 746 747 /* based on kernel32.GetBinaryTypeW() */ 748 static BOOL get_binary_type( const WCHAR *name, DWORD *type ) 749 { 750 HANDLE hfile, mapping; 751 NTSTATUS status; 752 753 hfile = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); 754 if (hfile == INVALID_HANDLE_VALUE) 755 return FALSE; 756 757 status = NtCreateSection( &mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY, NULL, NULL, PAGE_READONLY, 758 SEC_IMAGE, hfile ); 759 CloseHandle( hfile ); 760 761 switch (status) 762 { 763 case STATUS_SUCCESS: 764 { 765 SECTION_IMAGE_INFORMATION info; 766 767 status = NtQuerySection( mapping, SectionImageInformation, &info, sizeof(info), NULL ); 768 CloseHandle( mapping ); 769 if (status) return FALSE; 770 switch (info.Machine) 771 { 772 case IMAGE_FILE_MACHINE_I386: 773 case IMAGE_FILE_MACHINE_ARMNT: 774 *type = SCS_32BIT_BINARY; 775 return TRUE; 776 case IMAGE_FILE_MACHINE_AMD64: 777 case IMAGE_FILE_MACHINE_ARM64: 778 *type = SCS_64BIT_BINARY; 779 return TRUE; 780 default: 781 return FALSE; 782 } 783 } 784 case STATUS_INVALID_IMAGE_WIN_64: 785 *type = SCS_64BIT_BINARY; 786 return TRUE; 787 default: 788 return FALSE; 789 } 790 } 791 792 static custom_action_info *do_msidbCustomActionTypeDll( 793 MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action ) 794 { 795 custom_action_info *info; 796 RPC_STATUS status; 797 BOOL ret; 798 799 info = malloc( sizeof *info ); 800 if (!info) 801 return NULL; 802 803 msiobj_addref( &package->hdr ); 804 info->package = package; 805 info->type = type; 806 info->target = wcsdup( target ); 807 info->source = wcsdup( source ); 808 info->action = wcsdup( action ); 809 CoCreateGuid( &info->guid ); 810 811 EnterCriticalSection( &custom_action_cs ); 812 list_add_tail( &pending_custom_actions, &info->entry ); 813 LeaveCriticalSection( &custom_action_cs ); 814 815 if (!package->rpc_server_started) 816 { 817 WCHAR endpoint[12]; 818 819 swprintf(endpoint, ARRAY_SIZE(endpoint), L"msi%x", GetCurrentProcessId()); 820 status = RpcServerUseProtseqEpW((WCHAR *)L"ncalrpc", RPC_C_PROTSEQ_MAX_REQS_DEFAULT, 821 endpoint, NULL); 822 if (status != RPC_S_OK) 823 { 824 ERR("RpcServerUseProtseqEp failed: %#lx\n", status); 825 return NULL; 826 } 827 828 status = RpcServerRegisterIfEx(s_IWineMsiRemote_v0_0_s_ifspec, NULL, NULL, 829 RPC_IF_AUTOLISTEN, RPC_C_LISTEN_MAX_CALLS_DEFAULT, NULL); 830 if (status != RPC_S_OK) 831 { 832 ERR("RpcServerRegisterIfEx failed: %#lx\n", status); 833 return NULL; 834 } 835 836 info->package->rpc_server_started = 1; 837 } 838 839 ret = get_binary_type(source, &info->arch); 840 if (!ret) 841 info->arch = (sizeof(void *) == 8 ? SCS_64BIT_BINARY : SCS_32BIT_BINARY); 842 843 if (info->arch == SCS_64BIT_BINARY && sizeof(void *) == 4 && !is_wow64) 844 { 845 ERR("Attempt to run a 64-bit custom action inside a 32-bit WINEPREFIX.\n"); 846 free_custom_action_data( info ); 847 return NULL; 848 } 849 850 custom_start_server(package, info->arch); 851 852 info->handle = CreateThread(NULL, 0, custom_client_thread, info, 0, NULL); 853 if (!info->handle) 854 { 855 free_custom_action_data( info ); 856 return NULL; 857 } 858 859 return info; 860 } 861 862 static UINT HANDLE_CustomType1( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 863 INT type, const WCHAR *action ) 864 { 865 custom_action_info *info; 866 MSIBINARY *binary; 867 868 if (!(binary = get_temp_binary(package, source))) 869 return ERROR_FUNCTION_FAILED; 870 871 #if defined(__REACTOS__) && defined(_M_AMD64) 872 { 873 DWORD arch; 874 get_binary_type(binary->tmpfile, &arch); 875 if (arch == SCS_32BIT_BINARY) { 876 ERR("%s is a 32 bit custom action. Returning as ERROR_SUCCESS\n", debugstr_w(source)); 877 return ERROR_SUCCESS; // HACK: NO WOW64! return as executed though it's not true 878 } 879 } 880 #endif 881 882 TRACE("Calling function %s from %s\n", debugstr_w(target), debugstr_w(binary->tmpfile)); 883 884 if (!(info = do_msidbCustomActionTypeDll( package, type, binary->tmpfile, target, action ))) 885 return ERROR_FUNCTION_FAILED; 886 return wait_thread_handle( info ); 887 } 888 889 static HANDLE execute_command( const WCHAR *app, WCHAR *arg, const WCHAR *dir ) 890 { 891 STARTUPINFOW si; 892 PROCESS_INFORMATION info; 893 WCHAR *exe = NULL, *cmd = NULL, *p; 894 BOOL ret; 895 896 if (app) 897 { 898 int len_arg = 0; 899 DWORD len_exe; 900 901 if (!(exe = malloc( MAX_PATH * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE; 902 len_exe = SearchPathW( NULL, app, L".exe", MAX_PATH, exe, NULL ); 903 if (len_exe >= MAX_PATH) 904 { 905 free( exe ); 906 if (!(exe = malloc( len_exe * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE; 907 len_exe = SearchPathW( NULL, app, L".exe", len_exe, exe, NULL ); 908 } 909 if (!len_exe) 910 { 911 ERR("can't find executable %lu\n", GetLastError()); 912 free( exe ); 913 return INVALID_HANDLE_VALUE; 914 } 915 916 if (arg) len_arg = lstrlenW( arg ); 917 if (!(cmd = malloc( (len_exe + len_arg + 4) * sizeof(WCHAR) ))) 918 { 919 free( exe ); 920 return INVALID_HANDLE_VALUE; 921 } 922 p = cmd; 923 if (wcschr( exe, ' ' )) 924 { 925 *p++ = '\"'; 926 memcpy( p, exe, len_exe * sizeof(WCHAR) ); 927 p += len_exe; 928 *p++ = '\"'; 929 *p = 0; 930 } 931 else 932 { 933 lstrcpyW( p, exe ); 934 p += len_exe; 935 } 936 if (arg) 937 { 938 *p++ = ' '; 939 memcpy( p, arg, len_arg * sizeof(WCHAR) ); 940 p[len_arg] = 0; 941 } 942 } 943 memset( &si, 0, sizeof(STARTUPINFOW) ); 944 ret = CreateProcessW( exe, exe ? cmd : arg, NULL, NULL, FALSE, 0, NULL, dir, &si, &info ); 945 free( cmd ); 946 free( exe ); 947 if (!ret) 948 { 949 ERR("unable to execute command %lu\n", GetLastError()); 950 return INVALID_HANDLE_VALUE; 951 } 952 CloseHandle( info.hThread ); 953 return info.hProcess; 954 } 955 956 static UINT HANDLE_CustomType2( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 957 INT type, const WCHAR *action ) 958 { 959 MSIBINARY *binary; 960 HANDLE handle; 961 WCHAR *arg; 962 963 if (!(binary = get_temp_binary(package, source))) 964 return ERROR_FUNCTION_FAILED; 965 966 deformat_string( package, target, &arg ); 967 TRACE("exe %s arg %s\n", debugstr_w(binary->tmpfile), debugstr_w(arg)); 968 969 handle = execute_command( binary->tmpfile, arg, L"C:\\" ); 970 free( arg ); 971 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 972 return wait_process_handle( package, type, handle, action ); 973 } 974 975 static UINT HANDLE_CustomType17( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 976 INT type, const WCHAR *action ) 977 { 978 custom_action_info *info; 979 MSIFILE *file; 980 981 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 982 983 file = msi_get_loaded_file( package, source ); 984 if (!file) 985 { 986 ERR("invalid file key %s\n", debugstr_w( source )); 987 return ERROR_FUNCTION_FAILED; 988 } 989 990 #if defined(__REACTOS__) && defined(_M_AMD64) 991 { 992 DWORD arch; 993 get_binary_type(file->TargetPath, &arch); 994 if (arch == SCS_32BIT_BINARY) { 995 ERR("%s is a 32 bit custom action. Returning as ERROR_SUCCESS\n", debugstr_w(source)); 996 return ERROR_SUCCESS; // HACK: NO WOW64! return as executed though it's not true 997 } 998 } 999 #endif 1000 1001 if (!(info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action ))) 1002 return ERROR_FUNCTION_FAILED; 1003 return wait_thread_handle( info ); 1004 } 1005 1006 static UINT HANDLE_CustomType18( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1007 INT type, const WCHAR *action ) 1008 { 1009 MSIFILE *file; 1010 HANDLE handle; 1011 WCHAR *arg; 1012 1013 if (!(file = msi_get_loaded_file( package, source ))) return ERROR_FUNCTION_FAILED; 1014 1015 deformat_string( package, target, &arg ); 1016 TRACE("exe %s arg %s\n", debugstr_w(file->TargetPath), debugstr_w(arg)); 1017 1018 handle = execute_command( file->TargetPath, arg, L"C:\\" ); 1019 free( arg ); 1020 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1021 return wait_process_handle( package, type, handle, action ); 1022 } 1023 1024 static UINT HANDLE_CustomType19( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1025 INT type, const WCHAR *action ) 1026 { 1027 MSIRECORD *row = 0; 1028 LPWSTR deformated = NULL; 1029 1030 deformat_string( package, target, &deformated ); 1031 1032 /* first try treat the error as a number */ 1033 row = MSI_QueryGetRecord( package->db, L"SELECT `Message` FROM `Error` WHERE `Error` = '%s'", deformated ); 1034 if( row ) 1035 { 1036 LPCWSTR error = MSI_RecordGetString( row, 1 ); 1037 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE) 1038 MessageBoxW( NULL, error, NULL, MB_OK ); 1039 msiobj_release( &row->hdr ); 1040 } 1041 else if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE) 1042 MessageBoxW( NULL, deformated, NULL, MB_OK ); 1043 1044 free( deformated ); 1045 1046 return ERROR_INSTALL_FAILURE; 1047 } 1048 1049 static WCHAR *build_msiexec_args( const WCHAR *filename, const WCHAR *params ) 1050 { 1051 UINT len_filename = lstrlenW( filename ), len_params = lstrlenW( params ); 1052 UINT len = ARRAY_SIZE(L"/qb /i ") - 1; 1053 WCHAR *ret; 1054 1055 if (!(ret = malloc( (len + len_filename + len_params + 4) * sizeof(WCHAR) ))) return NULL; 1056 memcpy( ret, L"/qb /i ", sizeof(L"/qb /i ") ); 1057 ret[len++] = '"'; 1058 memcpy( ret + len, filename, len_filename * sizeof(WCHAR) ); 1059 len += len_filename; 1060 ret[len++] = '"'; 1061 ret[len++] = ' '; 1062 lstrcpyW( ret + len, params ); 1063 return ret; 1064 } 1065 1066 static UINT HANDLE_CustomType23( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1067 INT type, const WCHAR *action ) 1068 { 1069 WCHAR *dir, *filename, *args, *p; 1070 UINT len_dir, len_source = lstrlenW( source ); 1071 HANDLE handle; 1072 1073 if (!(dir = msi_dup_property( package->db, L"OriginalDatabase" ))) return ERROR_OUTOFMEMORY; 1074 if (!(p = wcsrchr( dir, '\\' )) && !(p = wcsrchr( dir, '/' ))) 1075 { 1076 free( dir ); 1077 return ERROR_FUNCTION_FAILED; 1078 } 1079 *p = 0; 1080 len_dir = p - dir; 1081 if (!(filename = malloc( (len_dir + len_source + 2) * sizeof(WCHAR) ))) 1082 { 1083 free( dir ); 1084 return ERROR_OUTOFMEMORY; 1085 } 1086 memcpy( filename, dir, len_dir * sizeof(WCHAR) ); 1087 filename[len_dir++] = '\\'; 1088 memcpy( filename + len_dir, source, len_source * sizeof(WCHAR) ); 1089 filename[len_dir + len_source] = 0; 1090 1091 if (!(args = build_msiexec_args( filename, target ))) 1092 { 1093 free( dir ); 1094 free( filename ); 1095 return ERROR_OUTOFMEMORY; 1096 } 1097 1098 TRACE("installing %s concurrently\n", debugstr_w(source)); 1099 1100 handle = execute_command( L"msiexec", args, dir ); 1101 free( dir ); 1102 free( filename ); 1103 free( args ); 1104 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1105 return wait_process_handle( package, type, handle, action ); 1106 } 1107 1108 static UINT write_substorage_to_file( MSIPACKAGE *package, const WCHAR *source, const WCHAR *filename ) 1109 { 1110 IStorage *src = NULL, *dst = NULL; 1111 UINT r = ERROR_FUNCTION_FAILED; 1112 HRESULT hr; 1113 1114 hr = StgCreateDocfile( filename, STGM_CREATE|STGM_TRANSACTED|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, &dst ); 1115 if (FAILED( hr )) 1116 { 1117 WARN( "can't open destination storage %s (%#lx)\n", debugstr_w(filename), hr ); 1118 goto done; 1119 } 1120 1121 hr = IStorage_OpenStorage( package->db->storage, source, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &src ); 1122 if (FAILED( hr )) 1123 { 1124 WARN( "can't open source storage %s (%#lx)\n", debugstr_w(source), hr ); 1125 goto done; 1126 } 1127 1128 hr = IStorage_CopyTo( src, 0, NULL, NULL, dst ); 1129 if (FAILED( hr )) 1130 { 1131 ERR( "failed to copy storage %s (%#lx)\n", debugstr_w(source), hr ); 1132 goto done; 1133 } 1134 1135 hr = IStorage_Commit( dst, 0 ); 1136 if (FAILED( hr )) 1137 ERR( "failed to commit storage (%#lx)\n", hr ); 1138 else 1139 r = ERROR_SUCCESS; 1140 1141 done: 1142 if (src) IStorage_Release( src ); 1143 if (dst) IStorage_Release( dst ); 1144 return r; 1145 } 1146 1147 static UINT HANDLE_CustomType7( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1148 INT type, const WCHAR *action ) 1149 { 1150 WCHAR *tmpfile, *args; 1151 MSIBINARY *binary = NULL; 1152 HANDLE handle; 1153 UINT r; 1154 1155 if (!(tmpfile = msi_create_temp_file( package->db ))) return ERROR_FUNCTION_FAILED; 1156 1157 r = write_substorage_to_file( package, source, tmpfile ); 1158 if (r != ERROR_SUCCESS) 1159 goto error; 1160 1161 if (!(binary = malloc( sizeof(*binary) ))) goto error; 1162 binary->source = NULL; 1163 binary->tmpfile = tmpfile; 1164 list_add_tail( &package->binaries, &binary->entry ); 1165 1166 if (!(args = build_msiexec_args( tmpfile, target ))) return ERROR_OUTOFMEMORY; 1167 1168 TRACE("installing %s concurrently\n", debugstr_w(source)); 1169 1170 handle = execute_command( L"msiexec", args, L"C:\\" ); 1171 free( args ); 1172 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1173 return wait_process_handle( package, type, handle, action ); 1174 1175 error: 1176 DeleteFileW( tmpfile ); 1177 free( tmpfile ); 1178 return ERROR_FUNCTION_FAILED; 1179 } 1180 1181 static UINT HANDLE_CustomType50( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1182 INT type, const WCHAR *action ) 1183 { 1184 WCHAR *exe, *arg; 1185 HANDLE handle; 1186 1187 if (!(exe = msi_dup_property( package->db, source ))) return ERROR_SUCCESS; 1188 1189 deformat_string( package, target, &arg ); 1190 TRACE("exe %s arg %s\n", debugstr_w(exe), debugstr_w(arg)); 1191 1192 handle = execute_command( exe, arg, L"C:\\" ); 1193 free( exe ); 1194 free( arg ); 1195 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1196 return wait_process_handle( package, type, handle, action ); 1197 } 1198 1199 static UINT HANDLE_CustomType34( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1200 INT type, const WCHAR *action ) 1201 { 1202 const WCHAR *workingdir = NULL; 1203 HANDLE handle; 1204 WCHAR *cmd; 1205 1206 if (source) 1207 { 1208 workingdir = msi_get_target_folder( package, source ); 1209 if (!workingdir) return ERROR_FUNCTION_FAILED; 1210 } 1211 deformat_string( package, target, &cmd ); 1212 if (!cmd) return ERROR_FUNCTION_FAILED; 1213 1214 TRACE("cmd %s dir %s\n", debugstr_w(cmd), debugstr_w(workingdir)); 1215 1216 handle = execute_command( NULL, cmd, workingdir ); 1217 free( cmd ); 1218 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1219 return wait_process_handle( package, type, handle, action ); 1220 } 1221 1222 static DWORD ACTION_CallScript( const GUID *guid ) 1223 { 1224 custom_action_info *info; 1225 MSIHANDLE hPackage; 1226 UINT r = ERROR_FUNCTION_FAILED; 1227 1228 info = find_action_by_guid( guid ); 1229 if (!info) 1230 { 1231 ERR("failed to find action %s\n", debugstr_guid( guid) ); 1232 return ERROR_FUNCTION_FAILED; 1233 } 1234 1235 TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) ); 1236 1237 hPackage = alloc_msihandle( &info->package->hdr ); 1238 if (hPackage) 1239 { 1240 r = call_script( hPackage, info->type, info->source, info->target, info->action ); 1241 TRACE("script returned %u\n", r); 1242 MsiCloseHandle( hPackage ); 1243 } 1244 else 1245 ERR("failed to create handle for %p\n", info->package ); 1246 1247 return r; 1248 } 1249 1250 static DWORD WINAPI ScriptThread( LPVOID arg ) 1251 { 1252 LPGUID guid = arg; 1253 DWORD rc; 1254 1255 TRACE("custom action (%#lx) started\n", GetCurrentThreadId() ); 1256 1257 rc = ACTION_CallScript( guid ); 1258 1259 TRACE("custom action (%#lx) returned %lu\n", GetCurrentThreadId(), rc ); 1260 1261 MsiCloseAllHandles(); 1262 return rc; 1263 } 1264 1265 static custom_action_info *do_msidbCustomActionTypeScript( 1266 MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action ) 1267 { 1268 custom_action_info *info; 1269 1270 info = malloc( sizeof *info ); 1271 if (!info) 1272 return NULL; 1273 1274 msiobj_addref( &package->hdr ); 1275 info->package = package; 1276 info->type = type; 1277 info->target = wcsdup( function ); 1278 info->source = wcsdup( script ); 1279 info->action = wcsdup( action ); 1280 CoCreateGuid( &info->guid ); 1281 1282 EnterCriticalSection( &custom_action_cs ); 1283 list_add_tail( &pending_custom_actions, &info->entry ); 1284 LeaveCriticalSection( &custom_action_cs ); 1285 1286 info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL ); 1287 if (!info->handle) 1288 { 1289 free_custom_action_data( info ); 1290 return NULL; 1291 } 1292 1293 return info; 1294 } 1295 1296 static UINT HANDLE_CustomType37_38( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1297 INT type, const WCHAR *action ) 1298 { 1299 custom_action_info *info; 1300 1301 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 1302 1303 info = do_msidbCustomActionTypeScript( package, type, target, NULL, action ); 1304 return wait_thread_handle( info ); 1305 } 1306 1307 static UINT HANDLE_CustomType5_6( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1308 INT type, const WCHAR *action ) 1309 { 1310 MSIRECORD *row = NULL; 1311 custom_action_info *info; 1312 CHAR *buffer = NULL; 1313 WCHAR *bufferw = NULL; 1314 DWORD sz = 0; 1315 UINT r; 1316 1317 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 1318 1319 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Binary` WHERE `Name` = '%s'", source); 1320 if (!row) 1321 return ERROR_FUNCTION_FAILED; 1322 1323 r = MSI_RecordReadStream(row, 2, NULL, &sz); 1324 if (r != ERROR_SUCCESS) goto done; 1325 1326 buffer = malloc( sz + 1 ); 1327 if (!buffer) 1328 { 1329 r = ERROR_FUNCTION_FAILED; 1330 goto done; 1331 } 1332 1333 r = MSI_RecordReadStream(row, 2, buffer, &sz); 1334 if (r != ERROR_SUCCESS) 1335 goto done; 1336 1337 buffer[sz] = 0; 1338 bufferw = strdupAtoW(buffer); 1339 if (!bufferw) 1340 { 1341 r = ERROR_FUNCTION_FAILED; 1342 goto done; 1343 } 1344 1345 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action ); 1346 r = wait_thread_handle( info ); 1347 1348 done: 1349 free(bufferw); 1350 free(buffer); 1351 msiobj_release(&row->hdr); 1352 return r; 1353 } 1354 1355 static UINT HANDLE_CustomType21_22( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1356 INT type, const WCHAR *action ) 1357 { 1358 custom_action_info *info; 1359 MSIFILE *file; 1360 HANDLE hFile; 1361 DWORD sz, szHighWord = 0, read; 1362 CHAR *buffer=NULL; 1363 WCHAR *bufferw=NULL; 1364 BOOL bRet; 1365 UINT r; 1366 1367 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 1368 1369 file = msi_get_loaded_file(package, source); 1370 if (!file) 1371 { 1372 ERR("invalid file key %s\n", debugstr_w(source)); 1373 return ERROR_FUNCTION_FAILED; 1374 } 1375 1376 hFile = msi_create_file( package, file->TargetPath, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ); 1377 if (hFile == INVALID_HANDLE_VALUE) return ERROR_FUNCTION_FAILED; 1378 1379 sz = GetFileSize(hFile, &szHighWord); 1380 if (sz == INVALID_FILE_SIZE || szHighWord != 0) 1381 { 1382 CloseHandle(hFile); 1383 return ERROR_FUNCTION_FAILED; 1384 } 1385 buffer = malloc( sz + 1 ); 1386 if (!buffer) 1387 { 1388 CloseHandle(hFile); 1389 return ERROR_FUNCTION_FAILED; 1390 } 1391 bRet = ReadFile(hFile, buffer, sz, &read, NULL); 1392 CloseHandle(hFile); 1393 if (!bRet) 1394 { 1395 r = ERROR_FUNCTION_FAILED; 1396 goto done; 1397 } 1398 buffer[read] = 0; 1399 bufferw = strdupAtoW(buffer); 1400 if (!bufferw) 1401 { 1402 r = ERROR_FUNCTION_FAILED; 1403 goto done; 1404 } 1405 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action ); 1406 r = wait_thread_handle( info ); 1407 1408 done: 1409 free(bufferw); 1410 free(buffer); 1411 return r; 1412 } 1413 1414 static UINT HANDLE_CustomType53_54( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1415 INT type, const WCHAR *action ) 1416 { 1417 custom_action_info *info; 1418 WCHAR *prop; 1419 1420 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 1421 1422 prop = msi_dup_property( package->db, source ); 1423 if (!prop) return ERROR_SUCCESS; 1424 1425 info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action ); 1426 free(prop); 1427 return wait_thread_handle( info ); 1428 } 1429 1430 static BOOL action_type_matches_script( UINT type, UINT script ) 1431 { 1432 switch (script) 1433 { 1434 case SCRIPT_NONE: 1435 return FALSE; 1436 case SCRIPT_INSTALL: 1437 return !(type & msidbCustomActionTypeCommit) && !(type & msidbCustomActionTypeRollback); 1438 case SCRIPT_COMMIT: 1439 return (type & msidbCustomActionTypeCommit); 1440 case SCRIPT_ROLLBACK: 1441 return (type & msidbCustomActionTypeRollback); 1442 default: 1443 ERR("unhandled script %u\n", script); 1444 } 1445 return FALSE; 1446 } 1447 1448 static UINT defer_custom_action( MSIPACKAGE *package, const WCHAR *action, UINT type ) 1449 { 1450 WCHAR *actiondata = msi_dup_property( package->db, action ); 1451 WCHAR *usersid = msi_dup_property( package->db, L"UserSID" ); 1452 WCHAR *prodcode = msi_dup_property( package->db, L"ProductCode" ); 1453 WCHAR *deferred = get_deferred_action( action, actiondata, usersid, prodcode ); 1454 1455 if (!deferred) 1456 { 1457 free( actiondata ); 1458 free( usersid ); 1459 free( prodcode ); 1460 return ERROR_OUTOFMEMORY; 1461 } 1462 if (type & msidbCustomActionTypeCommit) 1463 { 1464 TRACE("deferring commit action\n"); 1465 msi_schedule_action( package, SCRIPT_COMMIT, deferred ); 1466 } 1467 else if (type & msidbCustomActionTypeRollback) 1468 { 1469 TRACE("deferring rollback action\n"); 1470 msi_schedule_action( package, SCRIPT_ROLLBACK, deferred ); 1471 } 1472 else 1473 { 1474 TRACE("deferring install action\n"); 1475 msi_schedule_action( package, SCRIPT_INSTALL, deferred ); 1476 } 1477 1478 free( actiondata ); 1479 free( usersid ); 1480 free( prodcode ); 1481 free( deferred ); 1482 return ERROR_SUCCESS; 1483 } 1484 1485 UINT ACTION_CustomAction(MSIPACKAGE *package, const WCHAR *action) 1486 { 1487 UINT rc = ERROR_SUCCESS; 1488 MSIRECORD *row; 1489 UINT type; 1490 const WCHAR *source, *target, *ptr, *deferred_data = NULL; 1491 WCHAR *deformated = NULL; 1492 int len; 1493 1494 /* deferred action: [properties]Action */ 1495 if ((ptr = wcsrchr(action, ']'))) 1496 { 1497 deferred_data = action; 1498 action = ptr + 1; 1499 } 1500 1501 row = MSI_QueryGetRecord( package->db, L"SELECT * FROM `CustomAction` WHERE `Action` = '%s'", action ); 1502 if (!row) 1503 return ERROR_FUNCTION_NOT_CALLED; 1504 1505 type = MSI_RecordGetInteger(row,2); 1506 source = MSI_RecordGetString(row,3); 1507 target = MSI_RecordGetString(row,4); 1508 1509 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type, 1510 debugstr_w(source), debugstr_w(target)); 1511 1512 /* handle some of the deferred actions */ 1513 if (type & msidbCustomActionTypeTSAware) 1514 FIXME("msidbCustomActionTypeTSAware not handled\n"); 1515 1516 if (type & msidbCustomActionTypeInScript) 1517 { 1518 if (type & msidbCustomActionTypeNoImpersonate) 1519 WARN("msidbCustomActionTypeNoImpersonate not handled\n"); 1520 1521 if (!action_type_matches_script(type, package->script)) 1522 { 1523 rc = defer_custom_action( package, action, type ); 1524 goto end; 1525 } 1526 else 1527 { 1528 LPWSTR actiondata = msi_dup_property( package->db, action ); 1529 1530 if (type & msidbCustomActionTypeInScript) 1531 package->scheduled_action_running = TRUE; 1532 1533 if (type & msidbCustomActionTypeCommit) 1534 package->commit_action_running = TRUE; 1535 1536 if (type & msidbCustomActionTypeRollback) 1537 package->rollback_action_running = TRUE; 1538 1539 if (deferred_data) 1540 set_deferred_action_props(package, deferred_data); 1541 else if (actiondata) 1542 msi_set_property( package->db, L"CustomActionData", actiondata, -1 ); 1543 else 1544 msi_set_property( package->db, L"CustomActionData", L"", -1 ); 1545 1546 free(actiondata); 1547 } 1548 } 1549 else if (!check_execution_scheduling_options(package,action,type)) 1550 { 1551 rc = ERROR_SUCCESS; 1552 goto end; 1553 } 1554 1555 switch (type & CUSTOM_ACTION_TYPE_MASK) 1556 { 1557 case 1: /* DLL file stored in a Binary table stream */ 1558 rc = HANDLE_CustomType1( package, source, target, type, action ); 1559 break; 1560 case 2: /* EXE file stored in a Binary table stream */ 1561 rc = HANDLE_CustomType2( package, source, target, type, action ); 1562 break; 1563 case 5: 1564 case 6: /* JScript/VBScript file stored in a Binary table stream */ 1565 rc = HANDLE_CustomType5_6( package, source, target, type, action ); 1566 break; 1567 case 7: /* Concurrent install from substorage */ 1568 deformat_string( package, target, &deformated ); 1569 rc = HANDLE_CustomType7( package, source, target, type, action ); 1570 free( deformated ); 1571 break; 1572 case 17: 1573 rc = HANDLE_CustomType17( package, source, target, type, action ); 1574 break; 1575 case 18: /* EXE file installed with package */ 1576 rc = HANDLE_CustomType18( package, source, target, type, action ); 1577 break; 1578 case 19: /* Error that halts install */ 1579 rc = HANDLE_CustomType19( package, source, target, type, action ); 1580 break; 1581 case 21: /* JScript/VBScript file installed with the product */ 1582 case 22: 1583 rc = HANDLE_CustomType21_22( package, source, target, type, action ); 1584 break; 1585 case 23: /* Installs another package in the source tree */ 1586 deformat_string( package, target, &deformated ); 1587 rc = HANDLE_CustomType23( package, source, deformated, type, action ); 1588 free( deformated ); 1589 break; 1590 case 34: /* EXE to be run in specified directory */ 1591 rc = HANDLE_CustomType34( package, source, target, type, action ); 1592 break; 1593 case 35: /* Directory set with formatted text */ 1594 deformat_string( package, target, &deformated ); 1595 MSI_SetTargetPathW( package, source, deformated ); 1596 free( deformated ); 1597 break; 1598 case 37: /* JScript/VBScript text stored in target column */ 1599 case 38: 1600 rc = HANDLE_CustomType37_38( package, source, target, type, action ); 1601 break; 1602 case 50: /* EXE file specified by a property value */ 1603 rc = HANDLE_CustomType50( package, source, target, type, action ); 1604 break; 1605 case 51: /* Property set with formatted text */ 1606 if (!source) break; 1607 len = deformat_string( package, target, &deformated ); 1608 rc = msi_set_property( package->db, source, deformated, len ); 1609 if (rc == ERROR_SUCCESS && !wcscmp( source, L"SourceDir" )) msi_reset_source_folders( package ); 1610 free( deformated ); 1611 break; 1612 case 53: /* JScript/VBScript text specified by a property value */ 1613 case 54: 1614 rc = HANDLE_CustomType53_54( package, source, target, type, action ); 1615 break; 1616 default: 1617 FIXME( "unhandled action type %u (%s %s)\n", type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source), 1618 debugstr_w(target) ); 1619 } 1620 1621 end: 1622 package->scheduled_action_running = FALSE; 1623 package->commit_action_running = FALSE; 1624 package->rollback_action_running = FALSE; 1625 msiobj_release(&row->hdr); 1626 return rc; 1627 } 1628 1629 void ACTION_FinishCustomActions(const MSIPACKAGE* package) 1630 { 1631 struct list *item; 1632 HANDLE *wait_handles; 1633 unsigned int handle_count, i; 1634 custom_action_info *info, *cursor; 1635 1636 while ((item = list_head( &package->RunningActions ))) 1637 { 1638 struct running_action *action = LIST_ENTRY( item, struct running_action, entry ); 1639 1640 list_remove( &action->entry ); 1641 1642 TRACE("waiting for %s\n", debugstr_w( action->name ) ); 1643 msi_dialog_check_messages( action->handle ); 1644 1645 CloseHandle( action->handle ); 1646 free( action->name ); 1647 free( action ); 1648 } 1649 1650 EnterCriticalSection( &custom_action_cs ); 1651 1652 handle_count = list_count( &pending_custom_actions ); 1653 wait_handles = malloc( handle_count * sizeof(HANDLE) ); 1654 1655 handle_count = 0; 1656 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &pending_custom_actions, custom_action_info, entry ) 1657 { 1658 if (info->package == package ) 1659 { 1660 if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0)) 1661 handle_count++; 1662 } 1663 } 1664 1665 LeaveCriticalSection( &custom_action_cs ); 1666 1667 for (i = 0; i < handle_count; i++) 1668 { 1669 msi_dialog_check_messages( wait_handles[i] ); 1670 CloseHandle( wait_handles[i] ); 1671 } 1672 free( wait_handles ); 1673 1674 EnterCriticalSection( &custom_action_cs ); 1675 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &pending_custom_actions, custom_action_info, entry ) 1676 { 1677 if (info->package == package) 1678 free_custom_action_data( info ); 1679 } 1680 LeaveCriticalSection( &custom_action_cs ); 1681 } 1682 1683 UINT __cdecl s_remote_GetActionInfo(const GUID *guid, WCHAR **name, int *type, WCHAR **dll, char **func, MSIHANDLE *hinst) 1684 { 1685 custom_action_info *info; 1686 1687 info = find_action_by_guid(guid); 1688 if (!info) 1689 return ERROR_INVALID_DATA; 1690 1691 *name = wcsdup(info->action); 1692 *type = info->type; 1693 *hinst = alloc_msihandle(&info->package->hdr); 1694 *dll = wcsdup(info->source); 1695 *func = strdupWtoA(info->target); 1696 1697 return ERROR_SUCCESS; 1698 } 1699