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 TRACE("Calling function %s from %s\n", debugstr_w(target), debugstr_w(binary->tmpfile)); 871 872 if (!(info = do_msidbCustomActionTypeDll( package, type, binary->tmpfile, target, action ))) 873 return ERROR_FUNCTION_FAILED; 874 return wait_thread_handle( info ); 875 } 876 877 static HANDLE execute_command( const WCHAR *app, WCHAR *arg, const WCHAR *dir ) 878 { 879 STARTUPINFOW si; 880 PROCESS_INFORMATION info; 881 WCHAR *exe = NULL, *cmd = NULL, *p; 882 BOOL ret; 883 884 if (app) 885 { 886 int len_arg = 0; 887 DWORD len_exe; 888 889 if (!(exe = msi_alloc( MAX_PATH * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE; 890 len_exe = SearchPathW( NULL, app, L".exe", MAX_PATH, exe, NULL ); 891 if (len_exe >= MAX_PATH) 892 { 893 msi_free( exe ); 894 if (!(exe = msi_alloc( len_exe * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE; 895 len_exe = SearchPathW( NULL, app, L".exe", len_exe, exe, NULL ); 896 } 897 if (!len_exe) 898 { 899 ERR("can't find executable %lu\n", GetLastError()); 900 msi_free( exe ); 901 return INVALID_HANDLE_VALUE; 902 } 903 904 if (arg) len_arg = lstrlenW( arg ); 905 if (!(cmd = msi_alloc( (len_exe + len_arg + 4) * sizeof(WCHAR) ))) 906 { 907 msi_free( exe ); 908 return INVALID_HANDLE_VALUE; 909 } 910 p = cmd; 911 if (wcschr( exe, ' ' )) 912 { 913 *p++ = '\"'; 914 memcpy( p, exe, len_exe * sizeof(WCHAR) ); 915 p += len_exe; 916 *p++ = '\"'; 917 *p = 0; 918 } 919 else 920 { 921 lstrcpyW( p, exe ); 922 p += len_exe; 923 } 924 if (arg) 925 { 926 *p++ = ' '; 927 memcpy( p, arg, len_arg * sizeof(WCHAR) ); 928 p[len_arg] = 0; 929 } 930 } 931 memset( &si, 0, sizeof(STARTUPINFOW) ); 932 ret = CreateProcessW( exe, exe ? cmd : arg, NULL, NULL, FALSE, 0, NULL, dir, &si, &info ); 933 msi_free( cmd ); 934 msi_free( exe ); 935 if (!ret) 936 { 937 ERR("unable to execute command %lu\n", GetLastError()); 938 return INVALID_HANDLE_VALUE; 939 } 940 CloseHandle( info.hThread ); 941 return info.hProcess; 942 } 943 944 static UINT HANDLE_CustomType2( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 945 INT type, const WCHAR *action ) 946 { 947 MSIBINARY *binary; 948 HANDLE handle; 949 WCHAR *arg; 950 951 if (!(binary = get_temp_binary(package, source))) 952 return ERROR_FUNCTION_FAILED; 953 954 deformat_string( package, target, &arg ); 955 TRACE("exe %s arg %s\n", debugstr_w(binary->tmpfile), debugstr_w(arg)); 956 957 handle = execute_command( binary->tmpfile, arg, L"C:\\" ); 958 msi_free( arg ); 959 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 960 return wait_process_handle( package, type, handle, action ); 961 } 962 963 static UINT HANDLE_CustomType17( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 964 INT type, const WCHAR *action ) 965 { 966 msi_custom_action_info *info; 967 MSIFILE *file; 968 969 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 970 971 file = msi_get_loaded_file( package, source ); 972 if (!file) 973 { 974 ERR("invalid file key %s\n", debugstr_w( source )); 975 return ERROR_FUNCTION_FAILED; 976 } 977 978 if (!(info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action ))) 979 return ERROR_FUNCTION_FAILED; 980 return wait_thread_handle( info ); 981 } 982 983 static UINT HANDLE_CustomType18( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 984 INT type, const WCHAR *action ) 985 { 986 MSIFILE *file; 987 HANDLE handle; 988 WCHAR *arg; 989 990 if (!(file = msi_get_loaded_file( package, source ))) return ERROR_FUNCTION_FAILED; 991 992 deformat_string( package, target, &arg ); 993 TRACE("exe %s arg %s\n", debugstr_w(file->TargetPath), debugstr_w(arg)); 994 995 handle = execute_command( file->TargetPath, arg, L"C:\\" ); 996 msi_free( arg ); 997 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 998 return wait_process_handle( package, type, handle, action ); 999 } 1000 1001 static UINT HANDLE_CustomType19( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1002 INT type, const WCHAR *action ) 1003 { 1004 MSIRECORD *row = 0; 1005 LPWSTR deformated = NULL; 1006 1007 deformat_string( package, target, &deformated ); 1008 1009 /* first try treat the error as a number */ 1010 row = MSI_QueryGetRecord( package->db, L"SELECT `Message` FROM `Error` WHERE `Error` = '%s'", deformated ); 1011 if( row ) 1012 { 1013 LPCWSTR error = MSI_RecordGetString( row, 1 ); 1014 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE) 1015 MessageBoxW( NULL, error, NULL, MB_OK ); 1016 msiobj_release( &row->hdr ); 1017 } 1018 else if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE) 1019 MessageBoxW( NULL, deformated, NULL, MB_OK ); 1020 1021 msi_free( deformated ); 1022 1023 return ERROR_INSTALL_FAILURE; 1024 } 1025 1026 static WCHAR *build_msiexec_args( const WCHAR *filename, const WCHAR *params ) 1027 { 1028 UINT len_filename = lstrlenW( filename ), len_params = lstrlenW( params ); 1029 UINT len = ARRAY_SIZE(L"/qb /i ") - 1; 1030 WCHAR *ret; 1031 1032 if (!(ret = msi_alloc( (len + len_filename + len_params + 4) * sizeof(WCHAR) ))) return NULL; 1033 memcpy( ret, L"/qb /i ", sizeof(L"/qb /i ") ); 1034 ret[len++] = '"'; 1035 memcpy( ret + len, filename, len_filename * sizeof(WCHAR) ); 1036 len += len_filename; 1037 ret[len++] = '"'; 1038 ret[len++] = ' '; 1039 lstrcpyW( ret + len, params ); 1040 return ret; 1041 } 1042 1043 static UINT HANDLE_CustomType23( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1044 INT type, const WCHAR *action ) 1045 { 1046 WCHAR *dir, *filename, *args, *p; 1047 UINT len_dir, len_source = lstrlenW( source ); 1048 HANDLE handle; 1049 1050 if (!(dir = msi_dup_property( package->db, L"OriginalDatabase" ))) return ERROR_OUTOFMEMORY; 1051 if (!(p = wcsrchr( dir, '\\' )) && !(p = wcsrchr( dir, '/' ))) 1052 { 1053 msi_free( dir ); 1054 return ERROR_FUNCTION_FAILED; 1055 } 1056 *p = 0; 1057 len_dir = p - dir; 1058 if (!(filename = msi_alloc( (len_dir + len_source + 2) * sizeof(WCHAR) ))) 1059 { 1060 msi_free( dir ); 1061 return ERROR_OUTOFMEMORY; 1062 } 1063 memcpy( filename, dir, len_dir * sizeof(WCHAR) ); 1064 filename[len_dir++] = '\\'; 1065 memcpy( filename + len_dir, source, len_source * sizeof(WCHAR) ); 1066 filename[len_dir + len_source] = 0; 1067 1068 if (!(args = build_msiexec_args( filename, target ))) 1069 { 1070 msi_free( dir ); 1071 return ERROR_OUTOFMEMORY; 1072 } 1073 1074 TRACE("installing %s concurrently\n", debugstr_w(source)); 1075 1076 handle = execute_command( L"msiexec", args, dir ); 1077 msi_free( dir ); 1078 msi_free( args ); 1079 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1080 return wait_process_handle( package, type, handle, action ); 1081 } 1082 1083 static UINT write_substorage_to_file( MSIPACKAGE *package, const WCHAR *source, const WCHAR *filename ) 1084 { 1085 IStorage *src = NULL, *dst = NULL; 1086 UINT r = ERROR_FUNCTION_FAILED; 1087 HRESULT hr; 1088 1089 hr = StgCreateDocfile( filename, STGM_CREATE|STGM_TRANSACTED|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, &dst ); 1090 if (FAILED( hr )) 1091 { 1092 WARN( "can't open destination storage %s (%#lx)\n", debugstr_w(filename), hr ); 1093 goto done; 1094 } 1095 1096 hr = IStorage_OpenStorage( package->db->storage, source, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &src ); 1097 if (FAILED( hr )) 1098 { 1099 WARN( "can't open source storage %s (%#lx)\n", debugstr_w(source), hr ); 1100 goto done; 1101 } 1102 1103 hr = IStorage_CopyTo( src, 0, NULL, NULL, dst ); 1104 if (FAILED( hr )) 1105 { 1106 ERR( "failed to copy storage %s (%#lx)\n", debugstr_w(source), hr ); 1107 goto done; 1108 } 1109 1110 hr = IStorage_Commit( dst, 0 ); 1111 if (FAILED( hr )) 1112 ERR( "failed to commit storage (%#lx)\n", hr ); 1113 else 1114 r = ERROR_SUCCESS; 1115 1116 done: 1117 if (src) IStorage_Release( src ); 1118 if (dst) IStorage_Release( dst ); 1119 return r; 1120 } 1121 1122 static UINT HANDLE_CustomType7( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1123 INT type, const WCHAR *action ) 1124 { 1125 WCHAR *tmpfile, *args; 1126 MSIBINARY *binary = NULL; 1127 HANDLE handle; 1128 UINT r; 1129 1130 if (!(tmpfile = msi_create_temp_file( package->db ))) return ERROR_FUNCTION_FAILED; 1131 1132 r = write_substorage_to_file( package, source, tmpfile ); 1133 if (r != ERROR_SUCCESS) 1134 goto error; 1135 1136 if (!(binary = msi_alloc( sizeof(*binary) ))) goto error; 1137 binary->source = NULL; 1138 binary->tmpfile = tmpfile; 1139 list_add_tail( &package->binaries, &binary->entry ); 1140 1141 if (!(args = build_msiexec_args( tmpfile, target ))) return ERROR_OUTOFMEMORY; 1142 1143 TRACE("installing %s concurrently\n", debugstr_w(source)); 1144 1145 handle = execute_command( L"msiexec", args, L"C:\\" ); 1146 msi_free( args ); 1147 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1148 return wait_process_handle( package, type, handle, action ); 1149 1150 error: 1151 DeleteFileW( tmpfile ); 1152 msi_free( tmpfile ); 1153 return ERROR_FUNCTION_FAILED; 1154 } 1155 1156 static UINT HANDLE_CustomType50( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1157 INT type, const WCHAR *action ) 1158 { 1159 WCHAR *exe, *arg; 1160 HANDLE handle; 1161 1162 if (!(exe = msi_dup_property( package->db, source ))) return ERROR_SUCCESS; 1163 1164 deformat_string( package, target, &arg ); 1165 TRACE("exe %s arg %s\n", debugstr_w(exe), debugstr_w(arg)); 1166 1167 handle = execute_command( exe, arg, L"C:\\" ); 1168 msi_free( exe ); 1169 msi_free( arg ); 1170 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1171 return wait_process_handle( package, type, handle, action ); 1172 } 1173 1174 static UINT HANDLE_CustomType34( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1175 INT type, const WCHAR *action ) 1176 { 1177 const WCHAR *workingdir = NULL; 1178 HANDLE handle; 1179 WCHAR *cmd; 1180 1181 if (source) 1182 { 1183 workingdir = msi_get_target_folder( package, source ); 1184 if (!workingdir) return ERROR_FUNCTION_FAILED; 1185 } 1186 deformat_string( package, target, &cmd ); 1187 if (!cmd) return ERROR_FUNCTION_FAILED; 1188 1189 TRACE("cmd %s dir %s\n", debugstr_w(cmd), debugstr_w(workingdir)); 1190 1191 handle = execute_command( NULL, cmd, workingdir ); 1192 msi_free( cmd ); 1193 if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS; 1194 return wait_process_handle( package, type, handle, action ); 1195 } 1196 1197 static DWORD ACTION_CallScript( const GUID *guid ) 1198 { 1199 msi_custom_action_info *info; 1200 MSIHANDLE hPackage; 1201 UINT r = ERROR_FUNCTION_FAILED; 1202 1203 info = find_action_by_guid( guid ); 1204 if (!info) 1205 { 1206 ERR("failed to find action %s\n", debugstr_guid( guid) ); 1207 return ERROR_FUNCTION_FAILED; 1208 } 1209 1210 TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) ); 1211 1212 hPackage = alloc_msihandle( &info->package->hdr ); 1213 if (hPackage) 1214 { 1215 r = call_script( hPackage, info->type, info->source, info->target, info->action ); 1216 TRACE("script returned %u\n", r); 1217 MsiCloseHandle( hPackage ); 1218 } 1219 else 1220 ERR("failed to create handle for %p\n", info->package ); 1221 1222 return r; 1223 } 1224 1225 static DWORD WINAPI ScriptThread( LPVOID arg ) 1226 { 1227 LPGUID guid = arg; 1228 DWORD rc; 1229 1230 TRACE("custom action (%#lx) started\n", GetCurrentThreadId() ); 1231 1232 rc = ACTION_CallScript( guid ); 1233 1234 TRACE("custom action (%#lx) returned %lu\n", GetCurrentThreadId(), rc ); 1235 1236 MsiCloseAllHandles(); 1237 return rc; 1238 } 1239 1240 static msi_custom_action_info *do_msidbCustomActionTypeScript( 1241 MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action ) 1242 { 1243 msi_custom_action_info *info; 1244 1245 info = msi_alloc( sizeof *info ); 1246 if (!info) 1247 return NULL; 1248 1249 msiobj_addref( &package->hdr ); 1250 info->package = package; 1251 info->type = type; 1252 info->target = strdupW( function ); 1253 info->source = strdupW( script ); 1254 info->action = strdupW( action ); 1255 CoCreateGuid( &info->guid ); 1256 1257 EnterCriticalSection( &msi_custom_action_cs ); 1258 list_add_tail( &msi_pending_custom_actions, &info->entry ); 1259 LeaveCriticalSection( &msi_custom_action_cs ); 1260 1261 info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL ); 1262 if (!info->handle) 1263 { 1264 free_custom_action_data( info ); 1265 return NULL; 1266 } 1267 1268 return info; 1269 } 1270 1271 static UINT HANDLE_CustomType37_38( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1272 INT type, const WCHAR *action ) 1273 { 1274 msi_custom_action_info *info; 1275 1276 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 1277 1278 info = do_msidbCustomActionTypeScript( package, type, target, NULL, action ); 1279 return wait_thread_handle( info ); 1280 } 1281 1282 static UINT HANDLE_CustomType5_6( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1283 INT type, const WCHAR *action ) 1284 { 1285 MSIRECORD *row = NULL; 1286 msi_custom_action_info *info; 1287 CHAR *buffer = NULL; 1288 WCHAR *bufferw = NULL; 1289 DWORD sz = 0; 1290 UINT r; 1291 1292 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 1293 1294 row = MSI_QueryGetRecord(package->db, L"SELECT * FROM `Binary` WHERE `Name` = '%s'", source); 1295 if (!row) 1296 return ERROR_FUNCTION_FAILED; 1297 1298 r = MSI_RecordReadStream(row, 2, NULL, &sz); 1299 if (r != ERROR_SUCCESS) goto done; 1300 1301 buffer = msi_alloc( sz + 1 ); 1302 if (!buffer) 1303 { 1304 r = ERROR_FUNCTION_FAILED; 1305 goto done; 1306 } 1307 1308 r = MSI_RecordReadStream(row, 2, buffer, &sz); 1309 if (r != ERROR_SUCCESS) 1310 goto done; 1311 1312 buffer[sz] = 0; 1313 bufferw = strdupAtoW(buffer); 1314 if (!bufferw) 1315 { 1316 r = ERROR_FUNCTION_FAILED; 1317 goto done; 1318 } 1319 1320 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action ); 1321 r = wait_thread_handle( info ); 1322 1323 done: 1324 msi_free(bufferw); 1325 msi_free(buffer); 1326 msiobj_release(&row->hdr); 1327 return r; 1328 } 1329 1330 static UINT HANDLE_CustomType21_22( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1331 INT type, const WCHAR *action ) 1332 { 1333 msi_custom_action_info *info; 1334 MSIFILE *file; 1335 HANDLE hFile; 1336 DWORD sz, szHighWord = 0, read; 1337 CHAR *buffer=NULL; 1338 WCHAR *bufferw=NULL; 1339 BOOL bRet; 1340 UINT r; 1341 1342 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 1343 1344 file = msi_get_loaded_file(package, source); 1345 if (!file) 1346 { 1347 ERR("invalid file key %s\n", debugstr_w(source)); 1348 return ERROR_FUNCTION_FAILED; 1349 } 1350 1351 hFile = msi_create_file( package, file->TargetPath, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ); 1352 if (hFile == INVALID_HANDLE_VALUE) return ERROR_FUNCTION_FAILED; 1353 1354 sz = GetFileSize(hFile, &szHighWord); 1355 if (sz == INVALID_FILE_SIZE || szHighWord != 0) 1356 { 1357 CloseHandle(hFile); 1358 return ERROR_FUNCTION_FAILED; 1359 } 1360 buffer = msi_alloc( sz + 1 ); 1361 if (!buffer) 1362 { 1363 CloseHandle(hFile); 1364 return ERROR_FUNCTION_FAILED; 1365 } 1366 bRet = ReadFile(hFile, buffer, sz, &read, NULL); 1367 CloseHandle(hFile); 1368 if (!bRet) 1369 { 1370 r = ERROR_FUNCTION_FAILED; 1371 goto done; 1372 } 1373 buffer[read] = 0; 1374 bufferw = strdupAtoW(buffer); 1375 if (!bufferw) 1376 { 1377 r = ERROR_FUNCTION_FAILED; 1378 goto done; 1379 } 1380 info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action ); 1381 r = wait_thread_handle( info ); 1382 1383 done: 1384 msi_free(bufferw); 1385 msi_free(buffer); 1386 return r; 1387 } 1388 1389 static UINT HANDLE_CustomType53_54( MSIPACKAGE *package, const WCHAR *source, const WCHAR *target, 1390 INT type, const WCHAR *action ) 1391 { 1392 msi_custom_action_info *info; 1393 WCHAR *prop; 1394 1395 TRACE("%s %s\n", debugstr_w(source), debugstr_w(target)); 1396 1397 prop = msi_dup_property( package->db, source ); 1398 if (!prop) return ERROR_SUCCESS; 1399 1400 info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action ); 1401 msi_free(prop); 1402 return wait_thread_handle( info ); 1403 } 1404 1405 static BOOL action_type_matches_script( UINT type, UINT script ) 1406 { 1407 switch (script) 1408 { 1409 case SCRIPT_NONE: 1410 return FALSE; 1411 case SCRIPT_INSTALL: 1412 return !(type & msidbCustomActionTypeCommit) && !(type & msidbCustomActionTypeRollback); 1413 case SCRIPT_COMMIT: 1414 return (type & msidbCustomActionTypeCommit); 1415 case SCRIPT_ROLLBACK: 1416 return (type & msidbCustomActionTypeRollback); 1417 default: 1418 ERR("unhandled script %u\n", script); 1419 } 1420 return FALSE; 1421 } 1422 1423 static UINT defer_custom_action( MSIPACKAGE *package, const WCHAR *action, UINT type ) 1424 { 1425 WCHAR *actiondata = msi_dup_property( package->db, action ); 1426 WCHAR *usersid = msi_dup_property( package->db, L"UserSID" ); 1427 WCHAR *prodcode = msi_dup_property( package->db, L"ProductCode" ); 1428 WCHAR *deferred = msi_get_deferred_action( action, actiondata, usersid, prodcode ); 1429 1430 if (!deferred) 1431 { 1432 msi_free( actiondata ); 1433 msi_free( usersid ); 1434 msi_free( prodcode ); 1435 return ERROR_OUTOFMEMORY; 1436 } 1437 if (type & msidbCustomActionTypeCommit) 1438 { 1439 TRACE("deferring commit action\n"); 1440 msi_schedule_action( package, SCRIPT_COMMIT, deferred ); 1441 } 1442 else if (type & msidbCustomActionTypeRollback) 1443 { 1444 TRACE("deferring rollback action\n"); 1445 msi_schedule_action( package, SCRIPT_ROLLBACK, deferred ); 1446 } 1447 else 1448 { 1449 TRACE("deferring install action\n"); 1450 msi_schedule_action( package, SCRIPT_INSTALL, deferred ); 1451 } 1452 1453 msi_free( actiondata ); 1454 msi_free( usersid ); 1455 msi_free( prodcode ); 1456 msi_free( deferred ); 1457 return ERROR_SUCCESS; 1458 } 1459 1460 UINT ACTION_CustomAction(MSIPACKAGE *package, const WCHAR *action) 1461 { 1462 UINT rc = ERROR_SUCCESS; 1463 MSIRECORD *row; 1464 UINT type; 1465 const WCHAR *source, *target, *ptr, *deferred_data = NULL; 1466 WCHAR *deformated = NULL; 1467 int len; 1468 1469 /* deferred action: [properties]Action */ 1470 if ((ptr = wcsrchr(action, ']'))) 1471 { 1472 deferred_data = action; 1473 action = ptr + 1; 1474 } 1475 1476 row = MSI_QueryGetRecord( package->db, L"SELECT * FROM `CustomAction` WHERE `Action` = '%s'", action ); 1477 if (!row) 1478 return ERROR_FUNCTION_NOT_CALLED; 1479 1480 type = MSI_RecordGetInteger(row,2); 1481 source = MSI_RecordGetString(row,3); 1482 target = MSI_RecordGetString(row,4); 1483 1484 TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type, 1485 debugstr_w(source), debugstr_w(target)); 1486 1487 /* handle some of the deferred actions */ 1488 if (type & msidbCustomActionTypeTSAware) 1489 FIXME("msidbCustomActionTypeTSAware not handled\n"); 1490 1491 if (type & msidbCustomActionTypeInScript) 1492 { 1493 if (type & msidbCustomActionTypeNoImpersonate) 1494 WARN("msidbCustomActionTypeNoImpersonate not handled\n"); 1495 1496 if (!action_type_matches_script(type, package->script)) 1497 { 1498 rc = defer_custom_action( package, action, type ); 1499 goto end; 1500 } 1501 else 1502 { 1503 LPWSTR actiondata = msi_dup_property( package->db, action ); 1504 1505 if (type & msidbCustomActionTypeInScript) 1506 package->scheduled_action_running = TRUE; 1507 1508 if (type & msidbCustomActionTypeCommit) 1509 package->commit_action_running = TRUE; 1510 1511 if (type & msidbCustomActionTypeRollback) 1512 package->rollback_action_running = TRUE; 1513 1514 if (deferred_data) 1515 set_deferred_action_props(package, deferred_data); 1516 else if (actiondata) 1517 msi_set_property( package->db, L"CustomActionData", actiondata, -1 ); 1518 else 1519 msi_set_property( package->db, L"CustomActionData", L"", -1 ); 1520 1521 msi_free(actiondata); 1522 } 1523 } 1524 else if (!check_execution_scheduling_options(package,action,type)) 1525 { 1526 rc = ERROR_SUCCESS; 1527 goto end; 1528 } 1529 1530 switch (type & CUSTOM_ACTION_TYPE_MASK) 1531 { 1532 case 1: /* DLL file stored in a Binary table stream */ 1533 rc = HANDLE_CustomType1( package, source, target, type, action ); 1534 break; 1535 case 2: /* EXE file stored in a Binary table stream */ 1536 rc = HANDLE_CustomType2( package, source, target, type, action ); 1537 break; 1538 case 5: 1539 case 6: /* JScript/VBScript file stored in a Binary table stream */ 1540 rc = HANDLE_CustomType5_6( package, source, target, type, action ); 1541 break; 1542 case 7: /* Concurrent install from substorage */ 1543 deformat_string( package, target, &deformated ); 1544 rc = HANDLE_CustomType7( package, source, target, type, action ); 1545 msi_free( deformated ); 1546 break; 1547 case 17: 1548 rc = HANDLE_CustomType17( package, source, target, type, action ); 1549 break; 1550 case 18: /* EXE file installed with package */ 1551 rc = HANDLE_CustomType18( package, source, target, type, action ); 1552 break; 1553 case 19: /* Error that halts install */ 1554 rc = HANDLE_CustomType19( package, source, target, type, action ); 1555 break; 1556 case 21: /* JScript/VBScript file installed with the product */ 1557 case 22: 1558 rc = HANDLE_CustomType21_22( package, source, target, type, action ); 1559 break; 1560 case 23: /* Installs another package in the source tree */ 1561 deformat_string( package, target, &deformated ); 1562 rc = HANDLE_CustomType23( package, source, deformated, type, action ); 1563 msi_free( deformated ); 1564 break; 1565 case 34: /* EXE to be run in specified directory */ 1566 rc = HANDLE_CustomType34( package, source, target, type, action ); 1567 break; 1568 case 35: /* Directory set with formatted text */ 1569 deformat_string( package, target, &deformated ); 1570 MSI_SetTargetPathW( package, source, deformated ); 1571 msi_free( deformated ); 1572 break; 1573 case 37: /* JScript/VBScript text stored in target column */ 1574 case 38: 1575 rc = HANDLE_CustomType37_38( package, source, target, type, action ); 1576 break; 1577 case 50: /* EXE file specified by a property value */ 1578 rc = HANDLE_CustomType50( package, source, target, type, action ); 1579 break; 1580 case 51: /* Property set with formatted text */ 1581 if (!source) break; 1582 len = deformat_string( package, target, &deformated ); 1583 rc = msi_set_property( package->db, source, deformated, len ); 1584 if (rc == ERROR_SUCCESS && !wcscmp( source, L"SourceDir" )) msi_reset_source_folders( package ); 1585 msi_free( deformated ); 1586 break; 1587 case 53: /* JScript/VBScript text specified by a property value */ 1588 case 54: 1589 rc = HANDLE_CustomType53_54( package, source, target, type, action ); 1590 break; 1591 default: 1592 FIXME( "unhandled action type %u (%s %s)\n", type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source), 1593 debugstr_w(target) ); 1594 } 1595 1596 end: 1597 package->scheduled_action_running = FALSE; 1598 package->commit_action_running = FALSE; 1599 package->rollback_action_running = FALSE; 1600 msiobj_release(&row->hdr); 1601 return rc; 1602 } 1603 1604 void ACTION_FinishCustomActions(const MSIPACKAGE* package) 1605 { 1606 struct list *item; 1607 HANDLE *wait_handles; 1608 unsigned int handle_count, i; 1609 msi_custom_action_info *info, *cursor; 1610 1611 while ((item = list_head( &package->RunningActions ))) 1612 { 1613 MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry ); 1614 1615 list_remove( &action->entry ); 1616 1617 TRACE("waiting for %s\n", debugstr_w( action->name ) ); 1618 msi_dialog_check_messages( action->handle ); 1619 1620 CloseHandle( action->handle ); 1621 msi_free( action->name ); 1622 msi_free( action ); 1623 } 1624 1625 EnterCriticalSection( &msi_custom_action_cs ); 1626 1627 handle_count = list_count( &msi_pending_custom_actions ); 1628 wait_handles = msi_alloc( handle_count * sizeof(HANDLE) ); 1629 1630 handle_count = 0; 1631 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry ) 1632 { 1633 if (info->package == package ) 1634 { 1635 if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0)) 1636 handle_count++; 1637 } 1638 } 1639 1640 LeaveCriticalSection( &msi_custom_action_cs ); 1641 1642 for (i = 0; i < handle_count; i++) 1643 { 1644 msi_dialog_check_messages( wait_handles[i] ); 1645 CloseHandle( wait_handles[i] ); 1646 } 1647 msi_free( wait_handles ); 1648 1649 EnterCriticalSection( &msi_custom_action_cs ); 1650 LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry ) 1651 { 1652 if (info->package == package) 1653 free_custom_action_data( info ); 1654 } 1655 LeaveCriticalSection( &msi_custom_action_cs ); 1656 } 1657 1658 UINT __cdecl s_remote_GetActionInfo(const GUID *guid, WCHAR **name, int *type, WCHAR **dll, char **func, MSIHANDLE *hinst) 1659 { 1660 msi_custom_action_info *info; 1661 1662 info = find_action_by_guid(guid); 1663 if (!info) 1664 return ERROR_INVALID_DATA; 1665 1666 *name = strdupW(info->action); 1667 *type = info->type; 1668 *hinst = alloc_msihandle(&info->package->hdr); 1669 *dll = strdupW(info->source); 1670 *func = strdupWtoA(info->target); 1671 1672 return ERROR_SUCCESS; 1673 } 1674