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