custom.c 43.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Custom Action processing for the Microsoft Installer (msi.dll)
 *
 * Copyright 2005 Aric Stewart for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23
#include "config.h"
#include "wine/port.h"

24 25
#define COBJMACROS

26
#include <stdarg.h>
27 28 29
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
30
#include "msidefs.h"
31
#include "winuser.h"
32 33
#include "objbase.h"
#include "oleauto.h"
34 35

#include "msipriv.h"
36
#include "msiserver.h"
37
#include "wine/debug.h"
38
#include "wine/unicode.h"
39
#include "wine/exception.h"
40 41 42 43 44 45 46

WINE_DEFAULT_DEBUG_CHANNEL(msi);

#define CUSTOM_ACTION_TYPE_MASK 0x3F

typedef struct tagMSIRUNNINGACTION
{
47
    struct list entry;
48 49 50 51 52
    HANDLE handle;
    BOOL   process;
    LPWSTR name;
} MSIRUNNINGACTION;

53
typedef UINT (WINAPI *MsiCustomActionEntryPoint)( MSIHANDLE );
54

55 56 57 58 59 60 61 62 63 64 65 66
static CRITICAL_SECTION msi_custom_action_cs;
static CRITICAL_SECTION_DEBUG msi_custom_action_cs_debug =
{
    0, 0, &msi_custom_action_cs,
    { &msi_custom_action_cs_debug.ProcessLocksList,
      &msi_custom_action_cs_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": msi_custom_action_cs") }
};
static CRITICAL_SECTION msi_custom_action_cs = { &msi_custom_action_cs_debug, -1, 0, 0, 0, 0 };

static struct list msi_pending_custom_actions = LIST_INIT( msi_pending_custom_actions );

67
UINT msi_schedule_action( MSIPACKAGE *package, UINT script, const WCHAR *action )
68 69 70 71
{
    UINT count;
    WCHAR **newbuf = NULL;

72
    if (script >= SCRIPT_MAX)
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    {
        FIXME("Unknown script requested %u\n", script);
        return ERROR_FUNCTION_FAILED;
    }
    TRACE("Scheduling action %s in script %u\n", debugstr_w(action), script);

    count = package->script->ActionCount[script];
    package->script->ActionCount[script]++;
    if (count != 0) newbuf = msi_realloc( package->script->Actions[script],
                                          package->script->ActionCount[script] * sizeof(WCHAR *) );
    else newbuf = msi_alloc( sizeof(WCHAR *) );

    newbuf[count] = strdupW( action );
    package->script->Actions[script] = newbuf;
    return ERROR_SUCCESS;
}

UINT msi_register_unique_action( MSIPACKAGE *package, const WCHAR *action )
{
    UINT count;
    WCHAR **newbuf = NULL;

    if (!package->script) return FALSE;

    TRACE("Registering %s as unique action\n", debugstr_w(action));

    count = package->script->UniqueActionsCount;
    package->script->UniqueActionsCount++;
    if (count != 0) newbuf = msi_realloc( package->script->UniqueActions,
                                          package->script->UniqueActionsCount * sizeof(WCHAR *) );
    else newbuf = msi_alloc( sizeof(WCHAR *) );

    newbuf[count] = strdupW( action );
    package->script->UniqueActions = newbuf;
    return ERROR_SUCCESS;
}

BOOL msi_action_is_unique( const MSIPACKAGE *package, const WCHAR *action )
{
    UINT i;

    if (!package->script) return FALSE;

    for (i = 0; i < package->script->UniqueActionsCount; i++)
    {
        if (!strcmpW( package->script->UniqueActions[i], action )) return TRUE;
    }
    return FALSE;
}

123 124 125 126 127
static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
{
    if (!package->script)
        return TRUE;

128
    if ((options & msidbCustomActionTypeClientRepeat) ==
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
            msidbCustomActionTypeClientRepeat)
    {
        if (!(package->script->InWhatSequence & SEQUENCE_UI &&
            package->script->InWhatSequence & SEQUENCE_EXEC))
        {
            TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
            return FALSE;
        }
    }
    else if (options & msidbCustomActionTypeFirstSequence)
    {
        if (package->script->InWhatSequence & SEQUENCE_UI &&
            package->script->InWhatSequence & SEQUENCE_EXEC )
        {
            TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
            return FALSE;
        }
    }
    else if (options & msidbCustomActionTypeOncePerProcess)
    {
149
        if (msi_action_is_unique(package, action))
150 151 152 153 154
        {
            TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
            return FALSE;
        }
        else
155
            msi_register_unique_action(package, action);
156 157 158 159 160
    }

    return TRUE;
}

161 162
/* stores the following properties before the action:
 *
163
 *    [CustomActionData<=>UserSID<=>ProductCode]Action
164
 */
165 166
static LPWSTR msi_get_deferred_action(LPCWSTR action, LPCWSTR actiondata,
                                      LPCWSTR usersid, LPCWSTR prodcode)
167 168 169 170
{
    LPWSTR deferred;
    DWORD len;

171 172 173
    static const WCHAR format[] = {
            '[','%','s','<','=','>','%','s','<','=','>','%','s',']','%','s',0
    };
174 175 176 177

    if (!actiondata)
        return strdupW(action);

178
    len = lstrlenW(action) + lstrlenW(actiondata) +
179 180
          lstrlenW(usersid) + lstrlenW(prodcode) +
          lstrlenW(format) - 7;
181 182
    deferred = msi_alloc(len * sizeof(WCHAR));

183
    sprintfW(deferred, format, actiondata, usersid, prodcode, action);
184 185 186
    return deferred;
}

187 188
static void set_deferred_action_props(MSIPACKAGE *package, LPWSTR deferred_data)
{
189
    LPWSTR end, beg = deferred_data + 1;
190

191 192 193
    static const WCHAR sep[] = {'<','=','>',0};

    end = strstrW(beg, sep);
194
    *end = '\0';
195
    msi_set_property(package->db, szCustomActionData, beg);
196
    beg = end + 3;
197

198
    end = strstrW(beg, sep);
199
    *end = '\0';
200
    msi_set_property(package->db, szUserSID, beg);
201
    beg = end + 3;
202 203 204

    end = strchrW(beg, ']');
    *end = '\0';
205
    msi_set_property(package->db, szProductCode, beg);
206 207
}

208
static MSIBINARY *create_temp_binary( MSIPACKAGE *package, LPCWSTR source, BOOL dll )
209
{
210 211 212 213
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
        '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
214 215
    MSIRECORD *row;
    MSIBINARY *binary;
216 217
    HANDLE file;
    CHAR buffer[1024];
218 219
    WCHAR fmt[MAX_PATH], tmpfile[MAX_PATH];
    DWORD sz = MAX_PATH, write;
220
    UINT r;
221

222
    if (msi_get_property(package->db, szTempFolder, fmt, &sz) != ERROR_SUCCESS)
223
        GetTempPathW(MAX_PATH, fmt);
224

225
    if (!GetTempFileNameW( fmt, szMsi, 0, tmpfile ))
226
    {
227 228
        TRACE("unable to create temp file %s (%u)\n", debugstr_w(tmpfile), GetLastError());
        return NULL;
229 230
    }

231 232
    row = MSI_QueryGetRecord(package->db, query, source);
    if (!row)
233
        return NULL;
234

235 236 237 238 239 240
    if (!(binary = msi_alloc_zero( sizeof(MSIBINARY) )))
    {
        msiobj_release( &row->hdr );
        return NULL;
    }
    file = CreateFileW( tmpfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
241
    if (file == INVALID_HANDLE_VALUE)
242
    {
243
        msiobj_release( &row->hdr );
244
        msi_free( binary );
245 246 247 248 249 250 251
        return NULL;
    }
    do
    {
        sz = sizeof(buffer);
        r = MSI_RecordReadStream( row, 2, buffer, &sz );
        if (r != ERROR_SUCCESS)
252
        {
253 254 255 256 257 258 259 260 261 262 263 264 265
            ERR("Failed to get stream\n");
            break;
        }
        WriteFile( file, buffer, sz, &write, NULL );
    } while (sz == sizeof buffer);

    CloseHandle( file );
    msiobj_release( &row->hdr );
    if (r != ERROR_SUCCESS)
    {
        DeleteFileW( tmpfile );
        msi_free( binary );
        return NULL;
266
    }
267

268 269 270
    /* keep a reference to prevent the dll from being unloaded */
    if (dll && !(binary->module = LoadLibraryW( tmpfile )))
    {
271
        WARN( "failed to load dll %s (%u)\n", debugstr_w( tmpfile ), GetLastError() );
272 273 274 275 276 277
    }
    binary->source = strdupW( source );
    binary->tmpfile = strdupW( tmpfile );
    list_add_tail( &package->binaries, &binary->entry );
    return binary;
}
278

279 280 281 282 283 284 285 286 287 288 289
static MSIBINARY *get_temp_binary( MSIPACKAGE *package, LPCWSTR source, BOOL dll )
{
    MSIBINARY *binary;

    LIST_FOR_EACH_ENTRY( binary, &package->binaries, MSIBINARY, entry )
    {
        if (!strcmpW( binary->source, source ))
            return binary;
    }

    return create_temp_binary( package, source, dll );
290 291
}

292
static void file_running_action(MSIPACKAGE* package, HANDLE Handle,
293 294
                                BOOL process, LPCWSTR name)
{
295 296
    MSIRUNNINGACTION *action;

297
    action = msi_alloc( sizeof(MSIRUNNINGACTION) );
298

299 300 301
    action->handle = Handle;
    action->process = process;
    action->name = strdupW(name);
302

303
    list_add_tail( &package->RunningActions, &action->entry );
304 305
}

306
static UINT custom_get_process_return( HANDLE process )
307
{
308
    DWORD rc = 0;
309

310
    GetExitCodeProcess( process, &rc );
311
    TRACE("exit code is %u\n", rc);
312 313 314 315 316
    if (rc != 0)
        return ERROR_FUNCTION_FAILED;
    return ERROR_SUCCESS;
}

317
static UINT custom_get_thread_return( MSIPACKAGE *package, HANDLE thread )
318 319 320 321
{
    DWORD rc = 0;

    GetExitCodeThread( thread, &rc );
322 323 324

    switch (rc)
    {
325 326 327 328 329 330 331
    case ERROR_FUNCTION_NOT_CALLED:
    case ERROR_SUCCESS:
    case ERROR_INSTALL_USEREXIT:
    case ERROR_INSTALL_FAILURE:
        return rc;
    case ERROR_NO_MORE_ITEMS:
        return ERROR_SUCCESS;
332 333 334
    case ERROR_INSTALL_SUSPEND:
        ACTION_ForceReboot( package );
        return ERROR_SUCCESS;
335
    default:
336
        ERR("Invalid Return Code %d\n",rc);
337
        return ERROR_INSTALL_FAILURE;
338 339 340
    }
}

341 342
static UINT wait_process_handle(MSIPACKAGE* package, UINT type,
                           HANDLE ProcessHandle, LPCWSTR name)
343 344 345
{
    UINT rc = ERROR_SUCCESS;

346
    if (!(type & msidbCustomActionTypeAsync))
347
    {
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
        TRACE("waiting for %s\n", debugstr_w(name));

        msi_dialog_check_messages(ProcessHandle);

        if (!(type & msidbCustomActionTypeContinue))
            rc = custom_get_process_return(ProcessHandle);

        CloseHandle(ProcessHandle);
    }
    else
    {
        TRACE("%s running in background\n", debugstr_w(name));

        if (!(type & msidbCustomActionTypeContinue))
            file_running_action(package, ProcessHandle, TRUE, name);
363
        else
364 365 366 367 368 369
            CloseHandle(ProcessHandle);
    }

    return rc;
}

370 371
typedef struct _msi_custom_action_info {
    struct list entry;
372
    LONG refs;
373
    MSIPACKAGE *package;
374 375
    LPWSTR source;
    LPWSTR target;
376 377 378
    HANDLE handle;
    LPWSTR action;
    INT type;
379
    GUID guid;
380 381
} msi_custom_action_info;

382
static void release_custom_action_data( msi_custom_action_info *info )
383
{
384
    EnterCriticalSection( &msi_custom_action_cs );
385 386 387 388 389 390 391 392 393 394 395 396 397

    if (!--info->refs)
    {
        list_remove( &info->entry );
        if (info->handle)
            CloseHandle( info->handle );
        msi_free( info->action );
        msi_free( info->source );
        msi_free( info->target );
        msiobj_release( &info->package->hdr );
        msi_free( info );
    }

398
    LeaveCriticalSection( &msi_custom_action_cs );
399 400
}

401 402 403 404 405 406
/* must be called inside msi_custom_action_cs if info is in the pending custom actions list */
static void addref_custom_action_data( msi_custom_action_info *info )
{
    info->refs++;
 }

407
static UINT wait_thread_handle( msi_custom_action_info *info )
408 409 410
{
    UINT rc = ERROR_SUCCESS;

411
    if (!(info->type & msidbCustomActionTypeAsync))
412
    {
413
        TRACE("waiting for %s\n", debugstr_w( info->action ));
414

415
        msi_dialog_check_messages( info->handle );
416

417
        if (!(info->type & msidbCustomActionTypeContinue))
418
            rc = custom_get_thread_return( info->package, info->handle );
419

420
        release_custom_action_data( info );
421
    }
422
    else
423
    {
424
        TRACE("%s running in background\n", debugstr_w( info->action ));
425 426 427 428 429
    }

    return rc;
}

430
static msi_custom_action_info *find_action_by_guid( const GUID *guid )
431 432 433 434 435 436 437 438 439 440
{
    msi_custom_action_info *info;
    BOOL found = FALSE;

    EnterCriticalSection( &msi_custom_action_cs );

    LIST_FOR_EACH_ENTRY( info, &msi_pending_custom_actions, msi_custom_action_info, entry )
    {
        if (IsEqualGUID( &info->guid, guid ))
        {
441
            addref_custom_action_data( info );
442 443 444 445 446 447 448 449 450 451 452 453
            found = TRUE;
            break;
        }
    }

    LeaveCriticalSection( &msi_custom_action_cs );

    if (!found)
        return NULL;

    return info;
}
454

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
static void handle_msi_break( LPCWSTR target )
{
    LPWSTR msg;
    WCHAR val[MAX_PATH];

    static const WCHAR MsiBreak[] = { 'M','s','i','B','r','e','a','k',0 };
    static const WCHAR WindowsInstaller[] = {
        'W','i','n','d','o','w','s',' ','I','n','s','t','a','l','l','e','r',0
    };

    static const WCHAR format[] = {
        'T','o',' ','d','e','b','u','g',' ','y','o','u','r',' ',
        'c','u','s','t','o','m',' ','a','c','t','i','o','n',',',' ',
        'a','t','t','a','c','h',' ','y','o','u','r',' ','d','e','b','u','g','g','e','r',' ',
        't','o',' ','p','r','o','c','e','s','s',' ','%','i',' ','(','0','x','%','X',')',' ',
        'a','n','d',' ','p','r','e','s','s',' ','O','K',0
    };

    if( !GetEnvironmentVariableW( MsiBreak, val, MAX_PATH ))
        return;

476
    if( strcmpiW( val, target ))
477 478 479 480 481 482 483 484 485 486 487 488
        return;

    msg = msi_alloc( (lstrlenW(format) + 10) * sizeof(WCHAR) );
    if (!msg)
        return;

    wsprintfW( msg, format, GetCurrentProcessId(), GetCurrentProcessId());
    MessageBoxW( NULL, msg, WindowsInstaller, MB_OK);
    msi_free(msg);
    DebugBreak();
}

489 490 491 492 493 494 495 496
static UINT get_action_info( const GUID *guid, INT *type, MSIHANDLE *handle,
                             BSTR *dll, BSTR *funcname,
                             IWineMsiRemotePackage **package )
{
    IClassFactory *cf = NULL;
    IWineMsiRemoteCustomAction *rca = NULL;
    HRESULT r;

497
    r = DllGetClassObject( &CLSID_WineMsiRemoteCustomAction,
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
                           &IID_IClassFactory, (LPVOID *)&cf );
    if (FAILED(r))
    {
        ERR("failed to get IClassFactory interface\n");
        return ERROR_FUNCTION_FAILED;
    }

    r = IClassFactory_CreateInstance( cf, NULL, &IID_IWineMsiRemoteCustomAction, (LPVOID *)&rca );
    if (FAILED(r))
    {
        ERR("failed to get IWineMsiRemoteCustomAction interface\n");
        return ERROR_FUNCTION_FAILED;
    }

    r = IWineMsiRemoteCustomAction_GetActionInfo( rca, guid, type, handle, dll, funcname, package );
    IWineMsiRemoteCustomAction_Release( rca );
    if (FAILED(r))
    {
        ERR("GetActionInfo failed\n");
        return ERROR_FUNCTION_FAILED;
    }

    return ERROR_SUCCESS;
}

523 524 525
#ifdef __i386__
extern UINT CUSTOMPROC_wrapper( MsiCustomActionEntryPoint proc, MSIHANDLE handle );
__ASM_GLOBAL_FUNC( CUSTOMPROC_wrapper,
526 527 528 529 530 531 532 533 534 535 536 537
    "pushl %ebp\n\t"
    __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
    __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
    "movl %esp,%ebp\n\t"
    __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
    "pushl 12(%ebp)\n\t"
    "movl 8(%ebp),%eax\n\t"
    "call *%eax\n\t"
    "leave\n\t"
    __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
    __ASM_CFI(".cfi_same_value %ebp\n\t")
    "ret" )
538 539 540
#else
static inline UINT CUSTOMPROC_wrapper( MsiCustomActionEntryPoint proc, MSIHANDLE handle )
{
541
    return proc(handle);
542 543 544
}
#endif

545
static DWORD ACTION_CallDllFunction( const GUID *guid )
546
{
547
    MsiCustomActionEntryPoint fn;
548
    MSIHANDLE hPackage, handle;
549 550
    HANDLE hModule;
    LPSTR proc;
551
    UINT r = ERROR_FUNCTION_FAILED;
552 553 554
    BSTR dll = NULL, function = NULL;
    INT type;
    IWineMsiRemotePackage *remote_package = NULL;
555

556
    TRACE("%s\n", debugstr_guid( guid ));
557

558 559 560
    r = get_action_info( guid, &type, &handle, &dll, &function, &remote_package );
    if (r != ERROR_SUCCESS)
        return r;
561

562
    hModule = LoadLibraryW( dll );
563
    if (!hModule)
564
    {
565 566
        WARN( "failed to load dll %s (%u)\n", debugstr_w( dll ), GetLastError() );
        return ERROR_SUCCESS;
567
    }
568

569
    proc = strdupWtoA( function );
570 571 572 573
    fn = (MsiCustomActionEntryPoint) GetProcAddress( hModule, proc );
    msi_free( proc );
    if (fn)
    {
574
        hPackage = alloc_msi_remote_handle( (IUnknown *)remote_package );
575 576
        if (hPackage)
        {
577 578 579
            IWineMsiRemotePackage_SetMsiHandle( remote_package, handle );
            TRACE("calling %s\n", debugstr_w( function ) );
            handle_msi_break( function );
580 581 582

            __TRY
            {
583
                r = CUSTOMPROC_wrapper( fn, hPackage );
584 585 586 587
            }
            __EXCEPT_PAGE_FAULT
            {
                ERR("Custom action (%s:%s) caused a page fault: %08x\n",
588
                    debugstr_w(dll), debugstr_w(function), GetExceptionCode());
589 590 591 592
                r = ERROR_SUCCESS;
            }
            __ENDTRY;

593
            MsiCloseHandle( hPackage );
594 595
        }
        else
596
            ERR("failed to create handle for %p\n", remote_package );
597 598
    }
    else
599
        ERR("GetProcAddress(%s) failed\n", debugstr_w( function ) );
600 601 602

    FreeLibrary(hModule);

603 604 605 606
    IWineMsiRemotePackage_Release( remote_package );
    SysFreeString( dll );
    SysFreeString( function );
    MsiCloseHandle( handle );
607

608
    return r;
609 610
}

611
static DWORD WINAPI DllThread( LPVOID arg )
612
{
613
    LPGUID guid = arg;
614
    DWORD rc = 0;
615

616
    TRACE("custom action (%x) started\n", GetCurrentThreadId() );
617

618
    rc = ACTION_CallDllFunction( guid );
619 620

    TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );
621 622 623 624 625

    MsiCloseAllHandles();
    return rc;
}

626
static DWORD ACTION_CAInstallPackage(const GUID *guid)
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
{
    msi_custom_action_info *info;
    UINT r = ERROR_FUNCTION_FAILED;
    INSTALLUILEVEL old_level;

    info = find_action_by_guid(guid);
    if (!info)
    {
        ERR("failed to find action %s\n", debugstr_guid(guid));
        return r;
    }

    old_level = MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
    r = MsiInstallProductW(info->source, info->target);
    MsiSetInternalUI(old_level, NULL);

643 644
    release_custom_action_data(info);

645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
    return r;
}

static DWORD WINAPI ConcurrentInstallThread(LPVOID arg)
{
    LPGUID guid = arg;
    DWORD rc;

    TRACE("concurrent installation (%x) started\n", GetCurrentThreadId());

    rc = ACTION_CAInstallPackage(guid);

    TRACE("concurrent installation (%x) returned %i\n", GetCurrentThreadId(), rc);

    MsiCloseAllHandles();
    return rc;
}

663
static msi_custom_action_info *do_msidbCustomActionTypeDll(
664
    MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action )
665
{
666 667 668 669 670
    msi_custom_action_info *info;

    info = msi_alloc( sizeof *info );
    if (!info)
        return NULL;
671 672

    msiobj_addref( &package->hdr );
673
    info->refs = 2; /* 1 for our caller and 1 for thread we created */
674
    info->package = package;
675
    info->type = type;
676 677
    info->target = strdupW( target );
    info->source = strdupW( source );
678
    info->action = strdupW( action );
679 680 681 682 683
    CoCreateGuid( &info->guid );

    EnterCriticalSection( &msi_custom_action_cs );
    list_add_tail( &msi_pending_custom_actions, &info->entry );
    LeaveCriticalSection( &msi_custom_action_cs );
684

685
    info->handle = CreateThread( NULL, 0, DllThread, &info->guid, 0, NULL );
686 687
    if (!info->handle)
    {
688 689 690
        /* release both references */
        release_custom_action_data( info );
        release_custom_action_data( info );
691 692
        return NULL;
    }
693

694
    return info;
695 696
}

697 698 699 700 701 702 703 704 705 706
static msi_custom_action_info *do_msidbCAConcurrentInstall(
    MSIPACKAGE *package, INT type, LPCWSTR source, LPCWSTR target, LPCWSTR action)
{
    msi_custom_action_info *info;

    info = msi_alloc( sizeof *info );
    if (!info)
        return NULL;

    msiobj_addref( &package->hdr );
707
    info->refs = 2; /* 1 for our caller and 1 for thread we created */
708 709 710 711 712 713 714 715 716 717 718 719 720 721
    info->package = package;
    info->type = type;
    info->target = strdupW( target );
    info->source = strdupW( source );
    info->action = strdupW( action );
    CoCreateGuid( &info->guid );

    EnterCriticalSection( &msi_custom_action_cs );
    list_add_tail( &msi_pending_custom_actions, &info->entry );
    LeaveCriticalSection( &msi_custom_action_cs );

    info->handle = CreateThread( NULL, 0, ConcurrentInstallThread, &info->guid, 0, NULL );
    if (!info->handle)
    {
722 723 724
        /* release both references */
        release_custom_action_data( info );
        release_custom_action_data( info );
725 726 727 728 729 730 731 732 733 734 735 736 737
        return NULL;
    }

    return info;
}

static UINT HANDLE_CustomType23(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
    msi_custom_action_info *info;
    WCHAR package_path[MAX_PATH];
    DWORD size;

738
    size = MAX_PATH;
739
    msi_get_property(package->db, szSourceDir, package_path, &size);
740
    lstrcatW(package_path, szBackSlash);
741 742 743 744 745
    lstrcatW(package_path, source);

    TRACE("Installing package %s concurrently\n", debugstr_w(package_path));

    info = do_msidbCAConcurrentInstall(package, type, package_path, target, action);
746
    return wait_thread_handle(info);
747 748
}

749
static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
750 751
                               LPCWSTR target, const INT type, LPCWSTR action)
{
752
    msi_custom_action_info *info;
753
    MSIBINARY *binary;
754

755 756
    if (!(binary = get_temp_binary( package, source, TRUE )))
        return ERROR_FUNCTION_FAILED;
757

758
    TRACE("Calling function %s from %s\n", debugstr_w(target), debugstr_w(binary->tmpfile));
759

760
    info = do_msidbCustomActionTypeDll( package, type, binary->tmpfile, target, action );
761
    return wait_thread_handle( info );
762 763
}

764
static HANDLE execute_command( const WCHAR *app, WCHAR *arg, const WCHAR *dir )
765
{
766
    static const WCHAR dotexeW[] = {'.','e','x','e',0};
767 768
    STARTUPINFOW si;
    PROCESS_INFORMATION info;
769
    WCHAR *exe = NULL, *cmd = NULL, *p;
770
    BOOL ret;
771

772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
    if (app)
    {
        int len_arg = 0;
        DWORD len_exe;

        if (!(exe = msi_alloc( MAX_PATH * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE;
        len_exe = SearchPathW( NULL, app, dotexeW, MAX_PATH, exe, NULL );
        if (len_exe >= MAX_PATH)
        {
            msi_free( exe );
            if (!(exe = msi_alloc( len_exe * sizeof(WCHAR) ))) return INVALID_HANDLE_VALUE;
            len_exe = SearchPathW( NULL, app, dotexeW, len_exe, exe, NULL );
        }
        if (!len_exe)
        {
            WARN("can't find executable %u\n", GetLastError());
            msi_free( exe );
            return INVALID_HANDLE_VALUE;
        }

        if (arg) len_arg = strlenW( arg );
        if (!(cmd = msi_alloc( (len_exe + len_arg + 4) * sizeof(WCHAR) )))
        {
            msi_free( exe );
            return INVALID_HANDLE_VALUE;
        }
        p = cmd;
        if (strchrW( exe, ' ' ))
        {
            *p++ = '\"';
            memcpy( p, exe, len_exe * sizeof(WCHAR) );
            p += len_exe;
            *p++ = '\"';
            *p = 0;
        }
        else
        {
            strcpyW( p, exe );
            p += len_exe;
        }
        if (arg)
        {
            *p++ = ' ';
            memcpy( p, arg, len_arg * sizeof(WCHAR) );
            p[len_arg] = 0;
        }
    }
819
    memset( &si, 0, sizeof(STARTUPINFOW) );
820 821 822
    ret = CreateProcessW( exe, exe ? cmd : arg, NULL, NULL, FALSE, 0, NULL, dir, &si, &info );
    msi_free( cmd );
    msi_free( exe );
823
    if (!ret)
824
    {
825 826
        WARN("unable to execute command %u\n", GetLastError());
        return INVALID_HANDLE_VALUE;
827
    }
828 829 830
    CloseHandle( info.hThread );
    return info.hProcess;
}
831

832 833 834 835 836 837
static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    MSIBINARY *binary;
    HANDLE handle;
    WCHAR *arg;
838

839
    if (!(binary = get_temp_binary( package, source, FALSE ))) return ERROR_FUNCTION_FAILED;
840

841 842
    deformat_string( package, target, &arg );
    TRACE("exe %s arg %s\n", debugstr_w(binary->tmpfile), debugstr_w(arg));
843

844 845 846 847
    handle = execute_command( binary->tmpfile, arg, szCRoot );
    msi_free( arg );
    if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
    return wait_process_handle( package, type, handle, action );
848 849
}

850 851 852
static UINT HANDLE_CustomType17(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
853
    msi_custom_action_info *info;
854 855 856 857
    MSIFILE *file;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

858
    file = msi_get_loaded_file( package, source );
859 860 861 862 863 864
    if (!file)
    {
        ERR("invalid file key %s\n", debugstr_w( source ));
        return ERROR_FUNCTION_FAILED;
    }

865
    info = do_msidbCustomActionTypeDll( package, type, file->TargetPath, target, action );
866
    return wait_thread_handle( info );
867 868
}

869 870 871
static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
872
    MSIFILE *file;
873 874
    HANDLE handle;
    WCHAR *arg;
875

876
    if (!(file = msi_get_loaded_file( package, source ))) return ERROR_FUNCTION_FAILED;
877

878 879
    deformat_string( package, target, &arg );
    TRACE("exe %s arg %s\n", debugstr_w(file->TargetPath), debugstr_w(arg));
880

881 882 883 884
    handle = execute_command( file->TargetPath, arg, szCRoot );
    msi_free( arg );
    if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
    return wait_process_handle( package, type, handle, action );
885 886
}

887 888 889 890 891 892
static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
    static const WCHAR query[] = {
      'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
      'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
893
      'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
894
      '%','s',0
895 896 897 898 899 900 901
    };
    MSIRECORD *row = 0;
    LPWSTR deformated = NULL;

    deformat_string( package, target, &deformated );

    /* first try treat the error as a number */
902 903
    row = MSI_QueryGetRecord( package->db, query, deformated );
    if( row )
904
    {
905
        LPCWSTR error = MSI_RecordGetString( row, 1 );
906
        if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
907
            MessageBoxW( NULL, error, NULL, MB_OK );
908
        msiobj_release( &row->hdr );
909
    }
910
    else if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
911
        MessageBoxW( NULL, deformated, NULL, MB_OK );
912

913
    msi_free( deformated );
914

915
    return ERROR_INSTALL_FAILURE;
916 917
}

918 919 920
static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
921 922
    WCHAR *exe, *arg;
    HANDLE handle;
923

924
    if (!(exe = msi_dup_property( package->db, source ))) return ERROR_SUCCESS;
925

926 927
    deformat_string( package, target, &arg );
    TRACE("exe %s arg %s\n", debugstr_w(exe), debugstr_w(arg));
928

929 930 931 932
    handle = execute_command( exe, arg, szCRoot );
    msi_free( arg );
    if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
    return wait_process_handle( package, type, handle, action );
933 934 935 936 937
}

static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
                                LPCWSTR target, const INT type, LPCWSTR action)
{
938
    const WCHAR *workingdir = NULL;
939 940
    HANDLE handle;
    WCHAR *cmd;
941

942 943 944 945 946
    if (source)
    {
        workingdir = msi_get_target_folder( package, source );
        if (!workingdir) return ERROR_FUNCTION_FAILED;
    }
947 948
    deformat_string( package, target, &cmd );
    if (!cmd) return ERROR_FUNCTION_FAILED;
949

950
    TRACE("cmd %s dir %s\n", debugstr_w(cmd), debugstr_w(workingdir));
951

952 953 954 955
    handle = execute_command( NULL, cmd, workingdir );
    msi_free( cmd );
    if (handle == INVALID_HANDLE_VALUE) return ERROR_SUCCESS;
    return wait_process_handle( package, type, handle, action );
956 957
}

958
static DWORD ACTION_CallScript( const GUID *guid )
959 960
{
    msi_custom_action_info *info;
961
    MSIHANDLE hPackage;
962
    UINT r = ERROR_FUNCTION_FAILED;
963 964 965 966 967

    info = find_action_by_guid( guid );
    if (!info)
    {
        ERR("failed to find action %s\n", debugstr_guid( guid) );
968
        return ERROR_FUNCTION_FAILED;
969 970
    }

971 972 973 974 975 976
    TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) );

    hPackage = alloc_msihandle( &info->package->hdr );
    if (hPackage)
    {
        r = call_script( hPackage, info->type, info->source, info->target, info->action );
977
        TRACE("script returned %u\n", r);
978 979 980 981
        MsiCloseHandle( hPackage );
    }
    else
        ERR("failed to create handle for %p\n", info->package );
982

983
    release_custom_action_data( info );
984
    return r;
985 986 987 988 989
}

static DWORD WINAPI ScriptThread( LPVOID arg )
{
    LPGUID guid = arg;
990
    DWORD rc;
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

    TRACE("custom action (%x) started\n", GetCurrentThreadId() );

    rc = ACTION_CallScript( guid );

    TRACE("custom action (%x) returned %i\n", GetCurrentThreadId(), rc );

    MsiCloseAllHandles();
    return rc;
}

static msi_custom_action_info *do_msidbCustomActionTypeScript(
    MSIPACKAGE *package, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action )
{
    msi_custom_action_info *info;

    info = msi_alloc( sizeof *info );
    if (!info)
        return NULL;

    msiobj_addref( &package->hdr );
1012
    info->refs = 2; /* 1 for our caller and 1 for thread we created */
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
    info->package = package;
    info->type = type;
    info->target = strdupW( function );
    info->source = strdupW( script );
    info->action = strdupW( action );
    CoCreateGuid( &info->guid );

    EnterCriticalSection( &msi_custom_action_cs );
    list_add_tail( &msi_pending_custom_actions, &info->entry );
    LeaveCriticalSection( &msi_custom_action_cs );

    info->handle = CreateThread( NULL, 0, ScriptThread, &info->guid, 0, NULL );
    if (!info->handle)
    {
1027 1028 1029
        /* release both references */
        release_custom_action_data( info );
        release_custom_action_data( info );
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
        return NULL;
    }

    return info;
}

static UINT HANDLE_CustomType37_38(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    msi_custom_action_info *info;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

    info = do_msidbCustomActionTypeScript( package, type, target, NULL, action );
1044
    return wait_thread_handle( info );
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
}

static UINT HANDLE_CustomType5_6(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',' ',
        '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
    MSIRECORD *row = 0;
    msi_custom_action_info *info;
    CHAR *buffer = NULL;
    WCHAR *bufferw = NULL;
    DWORD sz = 0;
    UINT r;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

    row = MSI_QueryGetRecord(package->db, query, source);
    if (!row)
        return ERROR_FUNCTION_FAILED;

    r = MSI_RecordReadStream(row, 2, NULL, &sz);
1068
    if (r != ERROR_SUCCESS) return r;
1069

1070 1071
    buffer = msi_alloc( sz + 1 );
    if (!buffer) return ERROR_FUNCTION_FAILED;
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107

    r = MSI_RecordReadStream(row, 2, buffer, &sz);
    if (r != ERROR_SUCCESS)
        goto done;

    buffer[sz] = 0;
    bufferw = strdupAtoW(buffer);
    if (!bufferw)
    {
        r = ERROR_FUNCTION_FAILED;
        goto done;
    }

    info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
    r = wait_thread_handle( info );

done:
    msi_free(bufferw);
    msi_free(buffer);
    return r;
}

static UINT HANDLE_CustomType21_22(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    msi_custom_action_info *info;
    MSIFILE *file;
    HANDLE hFile;
    DWORD sz, szHighWord = 0, read;
    CHAR *buffer=NULL;
    WCHAR *bufferw=NULL;
    BOOL bRet;
    UINT r;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

1108
    file = msi_get_loaded_file(package, source);
1109 1110
    if (!file)
    {
1111 1112
        ERR("invalid file key %s\n", debugstr_w(source));
        return ERROR_FUNCTION_FAILED;
1113 1114 1115
    }

    hFile = CreateFileW(file->TargetPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1116
    if (hFile == INVALID_HANDLE_VALUE) return ERROR_FUNCTION_FAILED;
1117 1118 1119 1120

    sz = GetFileSize(hFile, &szHighWord);
    if (sz == INVALID_FILE_SIZE || szHighWord != 0)
    {
1121 1122
        CloseHandle(hFile);
        return ERROR_FUNCTION_FAILED;
1123
    }
1124
    buffer = msi_alloc( sz + 1 );
1125 1126
    if (!buffer)
    {
1127 1128
        CloseHandle(hFile);
        return ERROR_FUNCTION_FAILED;
1129 1130 1131 1132 1133
    }
    bRet = ReadFile(hFile, buffer, sz, &read, NULL);
    CloseHandle(hFile);
    if (!bRet)
    {
1134
        r = ERROR_FUNCTION_FAILED;
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
        goto done;
    }
    buffer[read] = 0;
    bufferw = strdupAtoW(buffer);
    if (!bufferw)
    {
        r = ERROR_FUNCTION_FAILED;
        goto done;
    }
    info = do_msidbCustomActionTypeScript( package, type, bufferw, target, action );
    r = wait_thread_handle( info );

done:
    msi_free(bufferw);
    msi_free(buffer);
    return r;
}

static UINT HANDLE_CustomType53_54(MSIPACKAGE *package, LPCWSTR source,
                               LPCWSTR target, const INT type, LPCWSTR action)
{
    msi_custom_action_info *info;
    WCHAR *prop;

    TRACE("%s %s\n", debugstr_w(source), debugstr_w(target));

1161
    prop = msi_dup_property( package->db, source );
1162
    if (!prop) return ERROR_SUCCESS;
1163 1164 1165

    info = do_msidbCustomActionTypeScript( package, type, prop, NULL, action );
    msi_free(prop);
1166
    return wait_thread_handle( info );
1167 1168
}

1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
static BOOL action_type_matches_script( MSIPACKAGE *package, UINT type, UINT script )
{
    switch (script)
    {
    case SCRIPT_NONE:
    case SCRIPT_INSTALL:
        return !(type & msidbCustomActionTypeCommit) && !(type & msidbCustomActionTypeRollback);
    case SCRIPT_COMMIT:
        return (type & msidbCustomActionTypeCommit);
    case SCRIPT_ROLLBACK:
        return (type & msidbCustomActionTypeRollback);
    default:
        ERR("unhandled script %u\n", script);
    }
    return FALSE;
}

static UINT defer_custom_action( MSIPACKAGE *package, const WCHAR *action, UINT type )
{
    WCHAR *actiondata = msi_dup_property( package->db, action );
    WCHAR *usersid = msi_dup_property( package->db, szUserSID );
    WCHAR *prodcode = msi_dup_property( package->db, szProductCode );
    WCHAR *deferred = msi_get_deferred_action( action, actiondata, usersid, prodcode );

    if (!deferred)
    {
        msi_free( actiondata );
        msi_free( usersid );
        msi_free( prodcode );
        return ERROR_OUTOFMEMORY;
    }
    if (type & msidbCustomActionTypeCommit)
    {
        TRACE("deferring commit action\n");
        msi_schedule_action( package, SCRIPT_COMMIT, deferred );
    }
    else if (type & msidbCustomActionTypeRollback)
    {
        TRACE("deferring rollback action\n");
        msi_schedule_action( package, SCRIPT_ROLLBACK, deferred );
    }
    else
    {
        TRACE("deferring install action\n");
        msi_schedule_action( package, SCRIPT_INSTALL, deferred );
    }

    msi_free( actiondata );
    msi_free( usersid );
    msi_free( prodcode );
    msi_free( deferred );
    return ERROR_SUCCESS;
}

1223 1224
UINT ACTION_CustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script, BOOL execute)
{
1225 1226 1227 1228
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','C','u','s','t','o','m','A','c','t','i','o','n','`',' ','W','H','E','R','E',' ',
        '`','A','c','t','i' ,'o','n','`',' ','=',' ','\'','%','s','\'',0};
1229
    UINT rc = ERROR_SUCCESS;
1230
    MSIRECORD *row;
1231 1232 1233
    UINT type;
    LPCWSTR source, target;
    LPWSTR ptr, deferred_data = NULL;
1234
    LPWSTR deformated = NULL, action_copy = strdupW(action);
1235 1236 1237 1238 1239 1240 1241 1242

    /* deferred action: [properties]Action */
    if ((ptr = strrchrW(action_copy, ']')))
    {
        deferred_data = action_copy;
        action = ptr + 1;
    }

1243
    row = MSI_QueryGetRecord( package->db, query, action );
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
    if (!row)
    {
        msi_free(action_copy);
        return ERROR_CALL_NOT_IMPLEMENTED;
    }

    type = MSI_RecordGetInteger(row,2);
    source = MSI_RecordGetString(row,3);
    target = MSI_RecordGetString(row,4);

    TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
          debugstr_w(source), debugstr_w(target));

    /* handle some of the deferred actions */
    if (type & msidbCustomActionTypeTSAware)
        FIXME("msidbCustomActionTypeTSAware not handled\n");

    if (type & msidbCustomActionTypeInScript)
    {
        if (type & msidbCustomActionTypeNoImpersonate)
            WARN("msidbCustomActionTypeNoImpersonate not handled\n");

1266
        if (!execute || !action_type_matches_script( package, type, script ))
1267
        {
1268
            rc = defer_custom_action( package, action, type );
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342
            goto end;
        }
        else
        {
            LPWSTR actiondata = msi_dup_property( package->db, action );

            if (type & msidbCustomActionTypeInScript)
                package->scheduled_action_running = TRUE;

            if (type & msidbCustomActionTypeCommit)
                package->commit_action_running = TRUE;

            if (type & msidbCustomActionTypeRollback)
                package->rollback_action_running = TRUE;

            if (deferred_data)
                set_deferred_action_props(package, deferred_data);
            else if (actiondata)
                msi_set_property(package->db, szCustomActionData, actiondata);
            else
                msi_set_property(package->db, szCustomActionData, szEmpty);

            msi_free(actiondata);
        }
    }
    else if (!check_execution_scheduling_options(package,action,type))
    {
        rc = ERROR_SUCCESS;
        goto end;
    }

    switch (type & CUSTOM_ACTION_TYPE_MASK)
    {
        case 1: /* DLL file stored in a Binary table stream */
            rc = HANDLE_CustomType1(package,source,target,type,action);
            break;
        case 2: /* EXE file stored in a Binary table stream */
            rc = HANDLE_CustomType2(package,source,target,type,action);
            break;
        case 18: /*EXE file installed with package */
            rc = HANDLE_CustomType18(package,source,target,type,action);
            break;
        case 19: /* Error that halts install */
            rc = HANDLE_CustomType19(package,source,target,type,action);
            break;
        case 17:
            rc = HANDLE_CustomType17(package,source,target,type,action);
            break;
        case 23: /* installs another package in the source tree */
            deformat_string(package,target,&deformated);
            rc = HANDLE_CustomType23(package,source,deformated,type,action);
            msi_free(deformated);
            break;
        case 50: /*EXE file specified by a property value */
            rc = HANDLE_CustomType50(package,source,target,type,action);
            break;
        case 34: /*EXE to be run in specified directory */
            rc = HANDLE_CustomType34(package,source,target,type,action);
            break;
        case 35: /* Directory set with formatted text. */
            deformat_string(package,target,&deformated);
            MSI_SetTargetPathW(package, source, deformated);
            msi_free(deformated);
            break;
        case 51: /* Property set with formatted text. */
            if (!source)
                break;

            deformat_string(package,target,&deformated);
            rc = msi_set_property( package->db, source, deformated );
            if (rc == ERROR_SUCCESS && !strcmpW( source, szSourceDir ))
                msi_reset_folders( package, TRUE );
            msi_free(deformated);
            break;
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
    case 37: /* JScript/VBScript text stored in target column. */
    case 38:
        rc = HANDLE_CustomType37_38(package,source,target,type,action);
        break;
    case 5:
    case 6: /* JScript/VBScript file stored in a Binary table stream. */
        rc = HANDLE_CustomType5_6(package,source,target,type,action);
        break;
    case 21: /* JScript/VBScript file installed with the product. */
    case 22:
        rc = HANDLE_CustomType21_22(package,source,target,type,action);
        break;
    case 53: /* JScript/VBScript text specified by a property value. */
    case 54:
        rc = HANDLE_CustomType53_54(package,source,target,type,action);
        break;
    default:
        FIXME("unhandled action type %u (%s %s)\n", type & CUSTOM_ACTION_TYPE_MASK,
              debugstr_w(source), debugstr_w(target));
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372
    }

end:
    package->scheduled_action_running = FALSE;
    package->commit_action_running = FALSE;
    package->rollback_action_running = FALSE;
    msi_free(action_copy);
    msiobj_release(&row->hdr);
    return rc;
}

1373
void ACTION_FinishCustomActions(const MSIPACKAGE* package)
1374
{
1375
    struct list *item;
1376 1377 1378
    HANDLE *wait_handles;
    unsigned int handle_count, i;
    msi_custom_action_info *info, *cursor;
1379

1380
    while ((item = list_head( &package->RunningActions )))
1381
    {
1382 1383 1384 1385
        MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );

        list_remove( &action->entry );

1386 1387
        TRACE("waiting for %s\n", debugstr_w( action->name ) );
        msi_dialog_check_messages( action->handle );
1388

1389
        CloseHandle( action->handle );
1390 1391
        msi_free( action->name );
        msi_free( action );
1392
    }
1393

1394
    EnterCriticalSection( &msi_custom_action_cs );
1395

1396
    handle_count = list_count( &msi_pending_custom_actions );
1397
    wait_handles = msi_alloc( handle_count * sizeof(HANDLE) );
1398

1399 1400 1401 1402
    handle_count = 0;
    LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
    {
        if (info->package == package )
1403
        {
1404 1405
            if (DuplicateHandle(GetCurrentProcess(), info->handle, GetCurrentProcess(), &wait_handles[handle_count], SYNCHRONIZE, FALSE, 0))
                handle_count++;
1406
        }
1407
    }
1408

1409
    LeaveCriticalSection( &msi_custom_action_cs );
1410

1411
    for (i = 0; i < handle_count; i++)
1412
    {
1413
        msi_dialog_check_messages( wait_handles[i] );
1414 1415
        CloseHandle( wait_handles[i] );
    }
1416
    msi_free( wait_handles );
1417 1418 1419 1420 1421 1422 1423

    EnterCriticalSection( &msi_custom_action_cs );
    LIST_FOR_EACH_ENTRY_SAFE( info, cursor, &msi_pending_custom_actions, msi_custom_action_info, entry )
    {
        if (info->package == package) release_custom_action_data( info );
    }
    LeaveCriticalSection( &msi_custom_action_cs );
1424
}
1425 1426

typedef struct _msi_custom_remote_impl {
1427
    IWineMsiRemoteCustomAction IWineMsiRemoteCustomAction_iface;
1428 1429 1430
    LONG refs;
} msi_custom_remote_impl;

1431
static inline msi_custom_remote_impl *impl_from_IWineMsiRemoteCustomAction( IWineMsiRemoteCustomAction *iface )
1432
{
1433
    return CONTAINING_RECORD(iface, msi_custom_remote_impl, IWineMsiRemoteCustomAction_iface);
1434 1435 1436 1437 1438 1439 1440 1441
}

static HRESULT WINAPI mcr_QueryInterface( IWineMsiRemoteCustomAction *iface,
                REFIID riid,LPVOID *ppobj)
{
    if( IsEqualCLSID( riid, &IID_IUnknown ) ||
        IsEqualCLSID( riid, &IID_IWineMsiRemoteCustomAction ) )
    {
1442
        IWineMsiRemoteCustomAction_AddRef( iface );
1443 1444 1445 1446 1447 1448 1449 1450 1451
        *ppobj = iface;
        return S_OK;
    }

    return E_NOINTERFACE;
}

static ULONG WINAPI mcr_AddRef( IWineMsiRemoteCustomAction *iface )
{
1452
    msi_custom_remote_impl* This = impl_from_IWineMsiRemoteCustomAction( iface );
1453 1454 1455 1456 1457 1458

    return InterlockedIncrement( &This->refs );
}

static ULONG WINAPI mcr_Release( IWineMsiRemoteCustomAction *iface )
{
1459
    msi_custom_remote_impl* This = impl_from_IWineMsiRemoteCustomAction( iface );
1460 1461 1462 1463 1464 1465 1466 1467 1468
    ULONG r;

    r = InterlockedDecrement( &This->refs );
    if (r == 0)
        msi_free( This );
    return r;
}

static HRESULT WINAPI mcr_GetActionInfo( IWineMsiRemoteCustomAction *iface, LPCGUID custom_action_guid,
1469
         INT *type, MSIHANDLE *handle, BSTR *dll, BSTR *func, IWineMsiRemotePackage **remote_package )
1470 1471 1472 1473 1474 1475 1476
{
    msi_custom_action_info *info;

    info = find_action_by_guid( custom_action_guid );
    if (!info)
        return E_FAIL;

1477
    *type = info->type;
1478 1479 1480 1481
    *handle = alloc_msihandle( &info->package->hdr );
    *dll = SysAllocString( info->source );
    *func = SysAllocString( info->target );

1482
    release_custom_action_data( info );
1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501
    return create_msi_remote_package( NULL, (LPVOID *)remote_package );
}

static const IWineMsiRemoteCustomActionVtbl msi_custom_remote_vtbl =
{
    mcr_QueryInterface,
    mcr_AddRef,
    mcr_Release,
    mcr_GetActionInfo,
};

HRESULT create_msi_custom_remote( IUnknown *pOuter, LPVOID *ppObj )
{
    msi_custom_remote_impl* This;

    This = msi_alloc( sizeof *This );
    if (!This)
        return E_OUTOFMEMORY;

1502
    This->IWineMsiRemoteCustomAction_iface.lpVtbl = &msi_custom_remote_vtbl;
1503 1504 1505 1506 1507 1508
    This->refs = 1;

    *ppObj = This;

    return S_OK;
}