action.c 257 KB
Newer Older
1 2 3
/*
 * Implementation of the Microsoft Installer (msi.dll)
 *
4
 * Copyright 2004,2005 Aric Stewart for CodeWeavers
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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
 */

#include <stdarg.h>

23 24
#define COBJMACROS

25 26 27 28
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
29
#include "winsvc.h"
30
#include "odbcinst.h"
31
#include "wine/debug.h"
32
#include "msidefs.h"
33 34
#include "winuser.h"
#include "shlobj.h"
35 36 37
#include "objbase.h"
#include "mscoree.h"
#include "shlwapi.h"
38
#include "imagehlp.h"
39
#include "wine/unicode.h"
40
#include "winver.h"
41

42 43 44
#include "msipriv.h"
#include "resource.h"

45 46
#define REG_PROGRESS_VALUE 13200
#define COMPONENT_PROGRESS_VALUE 24000
47 48 49

WINE_DEFAULT_DEBUG_CHANNEL(msi);

50
static const WCHAR szCreateFolders[] =
51
    {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
52
static const WCHAR szCostFinalize[] =
53
    {'C','o','s','t','F','i','n','a','l','i','z','e',0};
54
static const WCHAR szWriteRegistryValues[] =
55
    {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
56
static const WCHAR szFileCost[] = 
57
    {'F','i','l','e','C','o','s','t',0};
58
static const WCHAR szInstallInitialize[] = 
59
    {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
60
static const WCHAR szInstallValidate[] = 
61
    {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
62
static const WCHAR szLaunchConditions[] = 
63
    {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
64
static const WCHAR szProcessComponents[] = 
65
    {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
66
static const WCHAR szRegisterTypeLibraries[] = 
67
    {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
68
static const WCHAR szCreateShortcuts[] = 
69
    {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
70
static const WCHAR szPublishProduct[] = 
71
    {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
72
static const WCHAR szWriteIniValues[] = 
73
    {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
74
static const WCHAR szSelfRegModules[] = 
75
    {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
76
static const WCHAR szPublishFeatures[] = 
77
    {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
78
static const WCHAR szRegisterProduct[] = 
79
    {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
80
static const WCHAR szInstallExecute[] = 
81
    {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
82
static const WCHAR szInstallExecuteAgain[] = 
83
    {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
84
static const WCHAR szInstallFinalize[] = 
85
    {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
86
static const WCHAR szForceReboot[] = 
87
    {'F','o','r','c','e','R','e','b','o','o','t',0};
88
static const WCHAR szResolveSource[] =
89
    {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
90
static const WCHAR szAllocateRegistrySpace[] = 
91
    {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
92
static const WCHAR szBindImage[] = 
93
    {'B','i','n','d','I','m','a','g','e',0};
94
static const WCHAR szDeleteServices[] = 
95
    {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
96
static const WCHAR szDisableRollback[] = 
97
    {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
98
static const WCHAR szExecuteAction[] = 
99
    {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
100
static const WCHAR szInstallAdminPackage[] = 
101
    {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
102
static const WCHAR szInstallSFPCatalogFile[] = 
103
    {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
104
static const WCHAR szIsolateComponents[] = 
105
    {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
106 107
static const WCHAR szMigrateFeatureStates[] =
    {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
108
static const WCHAR szInstallODBC[] = 
109
    {'I','n','s','t','a','l','l','O','D','B','C',0};
110
static const WCHAR szInstallServices[] = 
111
    {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112
static const WCHAR szPublishComponents[] = 
113
    {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114
static const WCHAR szRegisterComPlus[] =
115
    {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116
static const WCHAR szRegisterUser[] =
117
    {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118
static const WCHAR szRemoveEnvironmentStrings[] =
119 120 121
    {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
static const WCHAR szRemoveExistingProducts[] =
    {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122
static const WCHAR szRemoveFolders[] =
123
    {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124
static const WCHAR szRemoveIniValues[] =
125
    {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126
static const WCHAR szRemoveODBC[] =
127
    {'R','e','m','o','v','e','O','D','B','C',0};
128
static const WCHAR szRemoveRegistryValues[] =
129
    {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130
static const WCHAR szRemoveShortcuts[] =
131
    {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132
static const WCHAR szRMCCPSearch[] =
133
    {'R','M','C','C','P','S','e','a','r','c','h',0};
134
static const WCHAR szScheduleReboot[] =
135
    {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136
static const WCHAR szSelfUnregModules[] =
137
    {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138
static const WCHAR szSetODBCFolders[] =
139
    {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140
static const WCHAR szStartServices[] =
141
    {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142
static const WCHAR szStopServices[] =
143
    {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144
static const WCHAR szUnpublishComponents[] =
145
    {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146
static const WCHAR szUnpublishFeatures[] =
147
    {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 149
static const WCHAR szUnpublishProduct[] =
    {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
150
static const WCHAR szUnregisterComPlus[] =
151
    {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
152
static const WCHAR szUnregisterTypeLibraries[] =
153
    {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
154
static const WCHAR szValidateProductID[] =
155
    {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
156
static const WCHAR szWriteEnvironmentStrings[] =
157
    {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
158 159
static const WCHAR szINSTALL[] =
    {'I','N','S','T','A','L','L',0};
160

161
static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
162
{
163
    static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
164 165 166
        '`','A','c','t','i','o','n','T','e','x','t','`',' ','W','H','E','R','E',' ',
        '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
    MSIRECORD *row, *textrow;
167
    INT rc;
168 169 170 171 172 173 174 175 176

    textrow = MSI_QueryGetRecord(package->db, query, action);
    if (textrow)
    {
        description = MSI_RecordGetString(textrow, 2);
        template = MSI_RecordGetString(textrow, 3);
    }

    row = MSI_CreateRecord(3);
177
    if (!row) return -1;
178 179 180
    MSI_RecordSetStringW(row, 1, action);
    MSI_RecordSetStringW(row, 2, description);
    MSI_RecordSetStringW(row, 3, template);
181
    rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
182
    if (textrow) msiobj_release(&textrow->hdr);
183
    msiobj_release(&row->hdr);
184
    return rc;
185 186
}

187
static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
188
                          INT rc)
189
{
190
    MSIRECORD *row;
191
    WCHAR *template;
192

193
    template = msi_get_error_message(package->db, start ? MSIERR_INFO_ACTIONSTART : MSIERR_INFO_ACTIONENDED);
194 195

    row = MSI_CreateRecord(2);
196 197 198 199 200
    if (!row)
    {
        msi_free(template);
        return;
    }
201
    MSI_RecordSetStringW(row, 0, template);
202
    MSI_RecordSetStringW(row, 1, action);
203
    MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc);
204 205
    MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
    msiobj_release(&row->hdr);
206
    msi_free(template);
207
    if (!start) package->LastActionResult = rc;
208 209
}

210 211 212 213 214 215 216 217 218 219 220 221
enum parse_state
{
    state_whitespace,
    state_token,
    state_quote
};

static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
{
    enum parse_state state = state_quote;
    const WCHAR *p;
    WCHAR *out = value;
222 223
    BOOL ignore, in_quotes = FALSE;
    int count = 0, len = 0;
224 225 226

    for (p = str; *p; p++)
    {
227
        ignore = FALSE;
228 229 230 231 232 233
        switch (state)
        {
        case state_whitespace:
            switch (*p)
            {
            case ' ':
234 235
                in_quotes = TRUE;
                ignore = TRUE;
236
                len++;
237 238 239
                break;
            case '"':
                state = state_quote;
240
                if (in_quotes && p[1] != '\"') count--;
241 242 243 244
                else count++;
                break;
            default:
                state = state_token;
245
                in_quotes = TRUE;
246 247 248 249 250 251 252 253 254 255
                len++;
                break;
            }
            break;

        case state_token:
            switch (*p)
            {
            case '"':
                state = state_quote;
256
                if (in_quotes) count--;
257 258 259 260 261
                else count++;
                break;
            case ' ':
                state = state_whitespace;
                if (!count) goto done;
262
                in_quotes = TRUE;
263
                len++;
264 265
                break;
            default:
266
                if (count) in_quotes = TRUE;
267 268 269 270 271 272 273 274 275
                len++;
                break;
            }
            break;

        case state_quote:
            switch (*p)
            {
            case '"':
276
                if (in_quotes && p[1] != '\"') count--;
277 278 279 280
                else count++;
                break;
            case ' ':
                state = state_whitespace;
281
                if (!count || (count > 1 && !len)) goto done;
282
                in_quotes = TRUE;
283
                len++;
284 285 286
                break;
            default:
                state = state_token;
287
                if (count) in_quotes = TRUE;
288 289 290 291 292 293 294
                len++;
                break;
            }
            break;

        default: break;
        }
295
        if (!ignore && value) *out++ = *p;
296
        if (!count) in_quotes = FALSE;
297 298 299
    }

done:
300 301 302 303 304
    if (value)
    {
        if (!len) *value = 0;
        else *out = 0;
    }
305

306
    if(quotes) *quotes = count;
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    return p - str;
}

static void remove_quotes( WCHAR *str )
{
    WCHAR *p = str;
    int len = strlenW( str );

    while ((p = strchrW( p, '"' )))
    {
        memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
        p++;
    }
}

322 323
UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
                             BOOL preserve_case )
324
{
325
    LPCWSTR ptr, ptr2;
326
    int num_quotes;
327
    DWORD len;
328 329
    WCHAR *prop, *val;
    UINT r;
330 331 332 333 334 335 336

    if (!szCommandLine)
        return ERROR_SUCCESS;

    ptr = szCommandLine;
    while (*ptr)
    {
337 338
        while (*ptr == ' ') ptr++;
        if (!*ptr) break;
339

340 341
        ptr2 = strchrW( ptr, '=' );
        if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
342

343 344
        len = ptr2 - ptr;
        if (!len) return ERROR_INVALID_COMMAND_LINE;
345

346 347
        while (ptr[len - 1] == ' ') len--;

348 349 350 351
        prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
        memcpy( prop, ptr, len * sizeof(WCHAR) );
        prop[len] = 0;
        if (!preserve_case) struprW( prop );
352

353
        ptr2++;
354 355
        while (*ptr2 == ' ') ptr2++;

356
        num_quotes = 0;
357
        val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
358 359
        len = parse_prop( ptr2, val, &num_quotes );
        if (num_quotes % 2)
360
        {
361 362 363 364
            WARN("unbalanced quotes\n");
            msi_free( val );
            msi_free( prop );
            return ERROR_INVALID_COMMAND_LINE;
365
        }
366 367
        remove_quotes( val );
        TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
368

369
        r = msi_set_property( package->db, prop, val, -1 );
370
        if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
371
            msi_reset_source_folders( package );
372

373 374
        msi_free( val );
        msi_free( prop );
375

376
        ptr = ptr2 + len;
377 378 379 380 381
    }

    return ERROR_SUCCESS;
}

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
const WCHAR *msi_get_command_line_option(const WCHAR *cmd, const WCHAR *option, UINT *len)
{
    DWORD opt_len = strlenW(option);

    if (!cmd)
        return NULL;

    while (*cmd)
    {
        BOOL found = FALSE;

        while (*cmd == ' ') cmd++;
        if (!*cmd) break;

        if(!strncmpiW(cmd, option, opt_len))
            found = TRUE;

        cmd = strchrW( cmd, '=' );
        if(!cmd) break;
        cmd++;
        while (*cmd == ' ') cmd++;
        if (!*cmd) break;

        *len = parse_prop( cmd, NULL, NULL);
        if (found) return cmd;
        cmd += *len;
    }

    return NULL;
}

413
WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
414
{
415
    LPCWSTR pc;
416 417 418 419 420 421 422
    LPWSTR p, *ret = NULL;
    UINT count = 0;

    if (!str)
        return ret;

    /* count the number of substrings */
423
    for ( pc = str, count = 0; pc; count++ )
424
    {
425 426 427
        pc = strchrW( pc, sep );
        if (pc)
            pc++;
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
    }

    /* allocate space for an array of substring pointers and the substrings */
    ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
                     (lstrlenW(str)+1) * sizeof(WCHAR) );
    if (!ret)
        return ret;

    /* copy the string and set the pointers */
    p = (LPWSTR) &ret[count+1];
    lstrcpyW( p, str );
    for( count = 0; (ret[count] = p); count++ )
    {
        p = strchrW( p, sep );
        if (p)
            *p++ = 0;
    }

    return ret;
}

449
static BOOL ui_sequence_exists( MSIPACKAGE *package )
450
{
451 452 453
    static const WCHAR query [] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
454
        'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
455
    MSIQUERY *view;
456
    DWORD count = 0;
457

458
    if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
459
    {
460 461
        MSI_IterateRecords( view, &count, NULL, package );
        msiobj_release( &view->hdr );
462
    }
463
    return count != 0;
464 465
}

466
UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
467
{
468 469
    WCHAR *source, *check, *p, *db;
    DWORD len;
470

471 472
    if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
        return ERROR_OUTOFMEMORY;
473

474
    if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
475
    {
476 477
        msi_free(db);
        return ERROR_SUCCESS;
478
    }
479 480 481 482
    len = p - db + 2;
    source = msi_alloc( len * sizeof(WCHAR) );
    lstrcpynW( source, db, len );
    msi_free( db );
483

484
    check = msi_dup_property( package->db, szSourceDir );
485
    if (!check || replace)
486
    {
487
        UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
488
        if (r == ERROR_SUCCESS)
489
            msi_reset_source_folders( package );
490
    }
491 492
    msi_free( check );

493
    check = msi_dup_property( package->db, szSOURCEDIR );
494
    if (!check || replace)
495
        msi_set_property( package->db, szSOURCEDIR, source, -1 );
496 497 498 499 500 501 502

    msi_free( check );
    msi_free( source );

    return ERROR_SUCCESS;
}

503 504
static BOOL needs_ui_sequence(MSIPACKAGE *package)
{
505
    return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
506 507
}

508
UINT msi_set_context(MSIPACKAGE *package)
509
{
510 511 512 513 514 515 516 517 518
    UINT r = msi_locate_product( package->ProductCode, &package->Context );
    if (r != ERROR_SUCCESS)
    {
        int num = msi_get_property_int( package->db, szAllUsers, 0 );
        if (num == 1 || num == 2)
            package->Context = MSIINSTALLCONTEXT_MACHINE;
        else
            package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
    }
519 520 521
    return ERROR_SUCCESS;
}

522 523 524 525
static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
{
    UINT rc;
    LPCWSTR cond, action;
526
    MSIPACKAGE *package = param;
527 528 529 530 531

    action = MSI_RecordGetString(row,1);
    if (!action)
    {
        ERR("Error is retrieving action name\n");
532
        return ERROR_FUNCTION_FAILED;
533 534 535 536
    }

    /* check conditions */
    cond = MSI_RecordGetString(row,2);
537 538

    /* this is a hack to skip errors in the condition code */
539
    if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
540
    {
541 542
        TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
        return ERROR_SUCCESS;
543 544
    }

545
    rc = ACTION_PerformAction(package, action);
546

547 548
    msi_dialog_check_messages( NULL );

549 550 551 552
    if (rc == ERROR_FUNCTION_NOT_CALLED)
        rc = ERROR_SUCCESS;

    if (rc != ERROR_SUCCESS)
553
        ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
554

555 556 557 558 559 560
    if (package->need_reboot_now)
    {
        TRACE("action %s asked for immediate reboot, suspending installation\n",
              debugstr_w(action));
        rc = ACTION_ForceReboot( package );
    }
561 562 563
    return rc;
}

564
UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
565
{
566 567 568
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
         ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
569 570
         '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
         '`','S','e','q','u','e','n','c','e','`',0};
571 572
    MSIQUERY *view;
    UINT r;
573

574
    TRACE("%p %s\n", package, debugstr_w(table));
575

576
    r = MSI_OpenQuery( package->db, &view, query, table );
577 578
    if (r == ERROR_SUCCESS)
    {
579
        r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
580 581 582 583 584
        msiobj_release(&view->hdr);
    }
    return r;
}

585
static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package)
586
{
587 588 589 590
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
        '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
        'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
591
        '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','0',' ',
592 593 594
        'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
    MSIQUERY *view;
    UINT rc;
595

596
    if (package->ExecuteSequenceRun)
597 598 599 600 601
    {
        TRACE("Execute Sequence already Run\n");
        return ERROR_SUCCESS;
    }

602
    package->ExecuteSequenceRun = TRUE;
603

604
    rc = MSI_OpenQuery(package->db, &view, query);
605 606
    if (rc == ERROR_SUCCESS)
    {
607
        TRACE("Running the actions\n");
608

609
        msi_set_property( package->db, szSourceDir, NULL, -1 );
610
        rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611
        msiobj_release(&view->hdr);
612 613 614 615
    }
    return rc;
}

616
static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
617
{
618 619 620 621 622 623
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
        'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
        'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
    MSIQUERY *view;
624
    UINT rc;
625

626
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
627 628
    if (rc == ERROR_SUCCESS)
    {
629
        TRACE("Running the actions\n"); 
630
        rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
631
        msiobj_release(&view->hdr);
632 633 634 635
    }
    return rc;
}

636 637 638
/********************************************************
 * ACTION helper functions and functions that perform the actions
 *******************************************************/
639
static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action)
640 641
{
    UINT arc;
642
    INT uirc;
643

644 645 646
    uirc = ui_actionstart(package, action, NULL, NULL);
    if (uirc == IDCANCEL)
        return ERROR_INSTALL_USEREXIT;
647
    ui_actioninfo(package, action, TRUE, 0);
648
    arc = ACTION_CustomAction(package, action);
649
    uirc = !arc;
650 651

    if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
    {
        uirc = ACTION_ShowDialog(package, action);
        switch (uirc)
        {
        case -1:
            return ERROR_SUCCESS; /* stop immediately */
        case 0: arc = ERROR_FUNCTION_NOT_CALLED; break;
        case 1: arc = ERROR_SUCCESS; break;
        case 2: arc = ERROR_INSTALL_USEREXIT; break;
        case 3: arc = ERROR_INSTALL_FAILURE; break;
        case 4: arc = ERROR_INSTALL_SUSPEND; break;
        case 5: arc = ERROR_MORE_DATA; break;
        case 6: arc = ERROR_INVALID_HANDLE_STATE; break;
        case 7: arc = ERROR_INVALID_DATA; break;
        case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break;
        case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break;
        default: arc = ERROR_FUNCTION_FAILED; break;
        }
    }
671

672
    ui_actioninfo(package, action, FALSE, uirc);
673

674
    return arc;
675
}
676

677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
{
    MSICOMPONENT *comp;

    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
    {
        if (!strcmpW( Component, comp->Component )) return comp;
    }
    return NULL;
}

MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
{
    MSIFEATURE *feature;

    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
    {
        if (!strcmpW( Feature, feature->Feature )) return feature;
    }
    return NULL;
}

MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
{
    MSIFILE *file;

    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
    {
        if (!strcmpW( key, file->File )) return file;
    }
    return NULL;
}

MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
{
    MSIFOLDER *folder;

    LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
    {
        if (!strcmpW( dir, folder->Directory )) return folder;
    }
    return NULL;
}

721
/*
722 723
 * Recursively create all directories in the path.
 * shamelessly stolen from setupapi/queue.c
724
 */
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
BOOL msi_create_full_path( const WCHAR *path )
{
    BOOL ret = TRUE;
    WCHAR *new_path;
    int len;

    new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
    strcpyW( new_path, path );

    while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
    new_path[len - 1] = 0;

    while (!CreateDirectoryW( new_path, NULL ))
    {
        WCHAR *slash;
        DWORD last_error = GetLastError();
        if (last_error == ERROR_ALREADY_EXISTS) break;
        if (last_error != ERROR_PATH_NOT_FOUND)
        {
            ret = FALSE;
            break;
        }
        if (!(slash = strrchrW( new_path, '\\' )))
        {
            ret = FALSE;
            break;
        }
        len = slash - new_path;
        new_path[len] = 0;
        if (!msi_create_full_path( new_path ))
        {
            ret = FALSE;
            break;
        }
        new_path[len] = '\\';
    }
    msi_free( new_path );
    return ret;
}

void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
{
    MSIRECORD *row;

    row = MSI_CreateRecord( 4 );
    MSI_RecordSetInteger( row, 1, a );
    MSI_RecordSetInteger( row, 2, b );
    MSI_RecordSetInteger( row, 3, c );
    MSI_RecordSetInteger( row, 4, d );
    MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
    msiobj_release( &row->hdr );

    msi_dialog_check_messages( NULL );
}

780 781 782 783 784 785 786 787
INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
{
    if (!comp->Enabled)
    {
        TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
        return INSTALLSTATE_UNKNOWN;
    }
    if (package->need_rollback) return comp->Installed;
788 789 790
    if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
    {
        TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
791
        return INSTALLSTATE_UNKNOWN;
792
    }
793 794 795
    return comp->ActionRequest;
}

796 797 798 799 800 801
INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
{
    if (package->need_rollback) return feature->Installed;
    return feature->ActionRequest;
}

802 803
static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
{
804
    MSIPACKAGE *package = param;
805
    LPCWSTR dir, component, full_path;
806 807
    MSIRECORD *uirow;
    MSIFOLDER *folder;
808 809 810
    MSICOMPONENT *comp;

    component = MSI_RecordGetString(row, 2);
811 812 813
    if (!component)
        return ERROR_SUCCESS;

814
    comp = msi_get_loaded_component(package, component);
815 816 817
    if (!comp)
        return ERROR_SUCCESS;

818 819
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
820
    {
821
        TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
822 823
        return ERROR_SUCCESS;
    }
824 825 826 827

    dir = MSI_RecordGetString(row,1);
    if (!dir)
    {
828
        ERR("Unable to get folder id\n");
829 830 831
        return ERROR_SUCCESS;
    }

832 833
    uirow = MSI_CreateRecord(1);
    MSI_RecordSetStringW(uirow, 1, dir);
834
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
835 836
    msiobj_release(&uirow->hdr);

837
    full_path = msi_get_target_folder( package, dir );
838 839
    if (!full_path)
    {
840
        ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
841 842
        return ERROR_SUCCESS;
    }
843
    TRACE("folder is %s\n", debugstr_w(full_path));
844

845
    folder = msi_get_loaded_folder( package, dir );
846
    if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
847
    folder->State = FOLDER_STATE_CREATED;
848 849 850
    return ERROR_SUCCESS;
}

851
static UINT ACTION_CreateFolders(MSIPACKAGE *package)
852
{
853 854 855
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
856
    MSIQUERY *view;
857
    UINT rc;
858

859 860 861
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szCreateFolders);

862
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
863
    if (rc != ERROR_SUCCESS)
864
        return ERROR_SUCCESS;
865

866
    rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
867
    msiobj_release(&view->hdr);
868 869 870
    return rc;
}

871 872 873 874 875 876 877 878 879 880 881 882 883 884
static void remove_persistent_folder( MSIFOLDER *folder )
{
    FolderList *fl;

    LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
    {
        remove_persistent_folder( fl->folder );
    }
    if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
    {
        if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
    }
}

885 886 887
static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
888
    LPCWSTR dir, component, full_path;
889 890
    MSIRECORD *uirow;
    MSIFOLDER *folder;
891 892 893
    MSICOMPONENT *comp;

    component = MSI_RecordGetString(row, 2);
894 895 896
    if (!component)
        return ERROR_SUCCESS;

897
    comp = msi_get_loaded_component(package, component);
898 899 900
    if (!comp)
        return ERROR_SUCCESS;

901 902
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
903
    {
904
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
905 906
        return ERROR_SUCCESS;
    }
907 908 909 910 911 912 913 914

    dir = MSI_RecordGetString( row, 1 );
    if (!dir)
    {
        ERR("Unable to get folder id\n");
        return ERROR_SUCCESS;
    }

915
    full_path = msi_get_target_folder( package, dir );
916 917
    if (!full_path)
    {
918
        ERR("Unable to resolve folder %s\n", debugstr_w(dir));
919 920 921 922 923
        return ERROR_SUCCESS;
    }
    TRACE("folder is %s\n", debugstr_w(full_path));

    uirow = MSI_CreateRecord( 1 );
924
    MSI_RecordSetStringW( uirow, 1, dir );
925
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
926 927
    msiobj_release( &uirow->hdr );

928
    folder = msi_get_loaded_folder( package, dir );
929
    remove_persistent_folder( folder );
930 931 932 933 934
    return ERROR_SUCCESS;
}

static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
{
935 936 937
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
938 939 940
    MSIQUERY *view;
    UINT rc;

941 942 943
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveFolders);

944 945 946 947 948 949 950 951 952
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
    msiobj_release( &view->hdr );
    return rc;
}

953
static UINT load_component( MSIRECORD *row, LPVOID param )
954
{
955
    MSIPACKAGE *package = param;
956
    MSICOMPONENT *comp;
957

958
    comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
959
    if (!comp)
960 961 962
        return ERROR_FUNCTION_FAILED;

    list_add_tail( &package->components, &comp->entry );
963

964
    /* fill in the data */
965
    comp->Component = msi_dup_record_field( row, 1 );
966

967
    TRACE("Loading Component %s\n", debugstr_w(comp->Component));
968

969 970
    comp->ComponentId = msi_dup_record_field( row, 2 );
    comp->Directory = msi_dup_record_field( row, 3 );
971
    comp->Attributes = MSI_RecordGetInteger(row,4);
972 973
    comp->Condition = msi_dup_record_field( row, 5 );
    comp->KeyPath = msi_dup_record_field( row, 6 );
974

975
    comp->Installed = INSTALLSTATE_UNKNOWN;
976 977
    comp->Action = INSTALLSTATE_UNKNOWN;
    comp->ActionRequest = INSTALLSTATE_UNKNOWN;
978

979
    comp->assembly = msi_load_assembly( package, comp );
980 981 982
    return ERROR_SUCCESS;
}

983
UINT msi_load_all_components( MSIPACKAGE *package )
984 985
{
    static const WCHAR query[] = {
986 987
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','C','o','m','p','o','n','e','n','t','`',0};
988 989 990 991 992
    MSIQUERY *view;
    UINT r;

    if (!list_empty(&package->components))
        return ERROR_SUCCESS;
993

994 995 996 997
    r = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (r != ERROR_SUCCESS)
        return r;

998 999 1000 1001 1002 1003 1004
    if (!msi_init_assembly_caches( package ))
    {
        ERR("can't initialize assembly caches\n");
        msiobj_release( &view->hdr );
        return ERROR_FUNCTION_FAILED;
    }

1005 1006 1007
    r = MSI_IterateRecords(view, NULL, load_component, package);
    msiobj_release(&view->hdr);
    return r;
1008 1009
}

1010 1011
typedef struct {
    MSIPACKAGE *package;
1012
    MSIFEATURE *feature;
1013 1014
} _ilfs;

1015
static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1016 1017 1018
{
    ComponentList *cl;

1019
    cl = msi_alloc( sizeof (*cl) );
1020 1021
    if ( !cl )
        return ERROR_NOT_ENOUGH_MEMORY;
1022
    cl->component = comp;
1023
    list_add_tail( &feature->Components, &cl->entry );
1024 1025 1026 1027

    return ERROR_SUCCESS;
}

1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
{
    FeatureList *fl;

    fl = msi_alloc( sizeof(*fl) );
    if ( !fl )
        return ERROR_NOT_ENOUGH_MEMORY;
    fl->feature = child;
    list_add_tail( &parent->Children, &fl->entry );

    return ERROR_SUCCESS;
}

1041
static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1042
{
1043
    _ilfs* ilfs = param;
1044
    LPCWSTR component;
1045
    MSICOMPONENT *comp;
1046 1047 1048 1049

    component = MSI_RecordGetString(row,1);

    /* check to see if the component is already loaded */
1050
    comp = msi_get_loaded_component( ilfs->package, component );
1051
    if (!comp)
1052
    {
1053 1054
        WARN("ignoring unknown component %s\n", debugstr_w(component));
        return ERROR_SUCCESS;
1055
    }
1056 1057
    add_feature_component( ilfs->feature, comp );
    comp->Enabled = TRUE;
1058 1059 1060 1061 1062 1063

    return ERROR_SUCCESS;
}

static UINT load_feature(MSIRECORD * row, LPVOID param)
{
1064 1065
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
Aric Stewart's avatar
Aric Stewart committed
1066
         ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1067
         'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
Aric Stewart's avatar
Aric Stewart committed
1068
         '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1069 1070 1071
    MSIPACKAGE *package = param;
    MSIFEATURE *feature;
    MSIQUERY *view;
1072
    _ilfs ilfs;
1073
    UINT rc;
1074

1075 1076
    /* fill in the data */

1077
    feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1078 1079
    if (!feature)
        return ERROR_NOT_ENOUGH_MEMORY;
1080

1081
    list_init( &feature->Children );
1082
    list_init( &feature->Components );
1083
    
1084
    feature->Feature = msi_dup_record_field( row, 1 );
1085

1086
    TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1087

1088 1089 1090
    feature->Feature_Parent = msi_dup_record_field( row, 2 );
    feature->Title = msi_dup_record_field( row, 3 );
    feature->Description = msi_dup_record_field( row, 4 );
1091

1092
    if (!MSI_RecordIsNull(row,5))
1093
        feature->Display = MSI_RecordGetInteger(row,5);
1094
  
1095
    feature->Level= MSI_RecordGetInteger(row,6);
1096
    feature->Directory = msi_dup_record_field( row, 7 );
1097
    feature->Attributes = MSI_RecordGetInteger(row,8);
1098

1099
    feature->Installed = INSTALLSTATE_UNKNOWN;
1100 1101
    feature->Action = INSTALLSTATE_UNKNOWN;
    feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1102

1103
    list_add_tail( &package->features, &feature->entry );
1104 1105 1106

    /* load feature components */

1107
    rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1108
    if (rc != ERROR_SUCCESS)
1109
        return ERROR_SUCCESS;
1110

1111 1112 1113
    ilfs.package = package;
    ilfs.feature = feature;

1114
    rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1115
    msiobj_release(&view->hdr);
1116
    return rc;
1117 1118
}

1119 1120
static UINT find_feature_children(MSIRECORD * row, LPVOID param)
{
1121
    MSIPACKAGE *package = param;
1122 1123
    MSIFEATURE *parent, *child;

1124
    child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1125 1126 1127 1128 1129 1130
    if (!child)
        return ERROR_FUNCTION_FAILED;

    if (!child->Feature_Parent)
        return ERROR_SUCCESS;

1131
    parent = msi_get_loaded_feature( package, child->Feature_Parent );
1132 1133 1134 1135 1136 1137 1138
    if (!parent)
        return ERROR_FUNCTION_FAILED;

    add_feature_child( parent, child );
    return ERROR_SUCCESS;
}

1139
UINT msi_load_all_features( MSIPACKAGE *package )
1140 1141
{
    static const WCHAR query[] = {
1142 1143 1144
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
        '`','D','i','s','p','l','a','y','`',0};
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
    MSIQUERY *view;
    UINT r;

    if (!list_empty(&package->features))
        return ERROR_SUCCESS;
 
    r = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (r != ERROR_SUCCESS)
        return r;

    r = MSI_IterateRecords( view, NULL, load_feature, package );
1156
    if (r != ERROR_SUCCESS)
1157 1158
    {
        msiobj_release( &view->hdr );
1159
        return r;
1160
    }
1161
    r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1162 1163 1164 1165
    msiobj_release( &view->hdr );
    return r;
}

1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
{
    if (!p)
        return p;
    p = strchrW(p, ch);
    if (!p)
        return p;
    *p = 0;
    return p+1;
}

1177 1178 1179 1180 1181 1182 1183
static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
{
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
        '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
        'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
    MSIQUERY *view = NULL;
1184
    MSIRECORD *row = NULL;
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
    UINT r;

    TRACE("%s\n", debugstr_w(file->File));

    r = MSI_OpenQuery(package->db, &view, query, file->File);
    if (r != ERROR_SUCCESS)
        goto done;

    r = MSI_ViewExecute(view, NULL);
    if (r != ERROR_SUCCESS)
        goto done;

    r = MSI_ViewFetch(view, &row);
    if (r != ERROR_SUCCESS)
        goto done;

    file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
    file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
    file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
    file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
    file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);

done:
    if (view) msiobj_release(&view->hdr);
1209
    if (row) msiobj_release(&row->hdr);
1210 1211 1212
    return r;
}

1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
{
    MSIRECORD *row;
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
        '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
        '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};

    row = MSI_QueryGetRecord( package->db, query, file->Sequence );
    if (!row)
    {
        WARN("query failed\n");
        return ERROR_FUNCTION_FAILED;
    }

    file->disk_id = MSI_RecordGetInteger( row, 1 );
    msiobj_release( &row->hdr );
    return ERROR_SUCCESS;
}

1233
static UINT load_file(MSIRECORD *row, LPVOID param)
1234
{
1235
    MSIPACKAGE* package = param;
1236
    LPCWSTR component;
1237
    MSIFILE *file;
1238 1239 1240

    /* fill in the data */

1241
    file = msi_alloc_zero( sizeof (MSIFILE) );
1242 1243
    if (!file)
        return ERROR_NOT_ENOUGH_MEMORY;
1244
 
1245
    file->File = msi_dup_record_field( row, 1 );
1246

1247
    component = MSI_RecordGetString( row, 2 );
1248
    file->Component = msi_get_loaded_component( package, component );
1249

1250
    if (!file->Component)
1251 1252 1253 1254 1255 1256
    {
        WARN("Component not found: %s\n", debugstr_w(component));
        msi_free(file->File);
        msi_free(file);
        return ERROR_SUCCESS;
    }
1257

1258
    file->FileName = msi_dup_record_field( row, 3 );
1259
    msi_reduce_to_long_filename( file->FileName );
1260

1261
    file->ShortName = msi_dup_record_field( row, 3 );
1262
    file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1263
    
1264
    file->FileSize = MSI_RecordGetInteger( row, 4 );
1265 1266
    file->Version = msi_dup_record_field( row, 5 );
    file->Language = msi_dup_record_field( row, 6 );
1267 1268 1269
    file->Attributes = MSI_RecordGetInteger( row, 7 );
    file->Sequence = MSI_RecordGetInteger( row, 8 );

1270
    file->state = msifs_invalid;
1271

1272 1273 1274
    /* if the compressed bits are not set in the file attributes,
     * then read the information from the package word count property
     */
1275 1276 1277 1278 1279 1280
    if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
    {
        file->IsCompressed = FALSE;
    }
    else if (file->Attributes &
             (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1281 1282 1283 1284 1285 1286 1287 1288 1289
    {
        file->IsCompressed = TRUE;
    }
    else if (file->Attributes & msidbFileAttributesNoncompressed)
    {
        file->IsCompressed = FALSE;
    }
    else
    {
1290
        file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1291 1292
    }

1293
    load_file_hash(package, file);
1294
    load_file_disk_id(package, file);
1295

1296
    TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1297

1298
    list_add_tail( &package->files, &file->entry );
1299 1300 1301 1302
 
    return ERROR_SUCCESS;
}

1303
static UINT load_all_files(MSIPACKAGE *package)
1304
{
1305 1306 1307 1308 1309
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
        '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
        '`','S','e','q','u','e','n','c','e','`', 0};
    MSIQUERY *view;
1310 1311
    UINT rc;

1312 1313 1314
    if (!list_empty(&package->files))
        return ERROR_SUCCESS;

1315
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1316
    if (rc != ERROR_SUCCESS)
1317
        return ERROR_SUCCESS;
1318

1319
    rc = MSI_IterateRecords(view, NULL, load_file, package);
1320
    msiobj_release(&view->hdr);
1321
    return rc;
1322 1323
}

1324 1325 1326 1327 1328 1329 1330
static UINT load_media( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    UINT disk_id = MSI_RecordGetInteger( row, 1 );
    const WCHAR *cabinet = MSI_RecordGetString( row, 4 );

    /* FIXME: load external cabinets and directory sources too */
1331 1332 1333 1334
    if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
        return ERROR_SUCCESS;

    return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1335 1336 1337 1338
}

static UINT load_all_media( MSIPACKAGE *package )
{
1339 1340 1341 1342
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
        'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
        '`','D','i','s','k','I','d','`',0};
1343 1344 1345 1346
    MSIQUERY *view;
    UINT r;

    r = MSI_DatabaseOpenViewW( package->db, query, &view );
1347 1348
    if (r != ERROR_SUCCESS)
        return ERROR_SUCCESS;
1349

1350
    r = MSI_IterateRecords( view, NULL, load_media, package );
1351
    msiobj_release( &view->hdr );
1352
    return r;
1353 1354
}

1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373
static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
{
    static const WCHAR query[] =
        {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
         '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
         '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
    MSIRECORD *rec;

    if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
    {
        WARN("query failed\n");
        return ERROR_FUNCTION_FAILED;
    }

    patch->disk_id = MSI_RecordGetInteger( rec, 1 );
    msiobj_release( &rec->hdr );
    return ERROR_SUCCESS;
}

1374 1375 1376 1377
static UINT load_patch(MSIRECORD *row, LPVOID param)
{
    MSIPACKAGE *package = param;
    MSIFILEPATCH *patch;
1378
    const WCHAR *file_key;
1379 1380 1381 1382 1383

    patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
    if (!patch)
        return ERROR_NOT_ENOUGH_MEMORY;

1384
    file_key = MSI_RecordGetString( row, 1 );
1385
    patch->File = msi_get_loaded_file( package, file_key );
1386
    if (!patch->File)
1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401
    {
        ERR("Failed to find target for patch in File table\n");
        msi_free(patch);
        return ERROR_FUNCTION_FAILED;
    }

    patch->Sequence = MSI_RecordGetInteger( row, 2 );
    patch->PatchSize = MSI_RecordGetInteger( row, 3 );
    patch->Attributes = MSI_RecordGetInteger( row, 4 );

    /* FIXME:
     * Header field - for patch validation.
     * _StreamRef   - External key into MsiPatchHeaders (instead of the header field)
     */

1402 1403
    load_patch_disk_id( package, patch );

1404
    TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1405 1406 1407 1408 1409 1410 1411 1412

    list_add_tail( &package->filepatches, &patch->entry );

    return ERROR_SUCCESS;
}

static UINT load_all_patches(MSIPACKAGE *package)
{
1413 1414 1415 1416
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
        '`','S','e','q','u','e','n','c','e','`',0};
1417 1418 1419 1420 1421 1422
    MSIQUERY *view;
    UINT rc;

    if (!list_empty(&package->filepatches))
        return ERROR_SUCCESS;

1423
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1424 1425 1426 1427 1428
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, load_patch, package);
    msiobj_release(&view->hdr);
1429
    return rc;
1430 1431
}

1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457
static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
{
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
        '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
    MSIQUERY *view;

    folder->persistent = FALSE;
    if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
    {
        if (!MSI_ViewExecute( view, NULL ))
        {
            MSIRECORD *rec;
            if (!MSI_ViewFetch( view, &rec ))
            {
                TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
                folder->persistent = TRUE;
                msiobj_release( &rec->hdr );
            }
        }
        msiobj_release( &view->hdr );
    }
    return ERROR_SUCCESS;
}

1458 1459 1460 1461 1462 1463 1464
static UINT load_folder( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    static WCHAR szEmpty[] = { 0 };
    LPWSTR p, tgt_short, tgt_long, src_short, src_long;
    MSIFOLDER *folder;

1465 1466
    if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
    list_init( &folder->children );
1467
    folder->Directory = msi_dup_record_field( row, 1 );
1468 1469
    folder->Parent = msi_dup_record_field( row, 2 );
    p = msi_dup_record_field(row, 3);
1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481

    TRACE("%s\n", debugstr_w(folder->Directory));

    /* split src and target dir */
    tgt_short = p;
    src_short = folder_split_path( p, ':' );

    /* split the long and short paths */
    tgt_long = folder_split_path( tgt_short, '|' );
    src_long = folder_split_path( src_short, '|' );

    /* check for no-op dirs */
1482
    if (tgt_short && !strcmpW( szDot, tgt_short ))
1483
        tgt_short = szEmpty;
1484
    if (src_short && !strcmpW( szDot, src_short ))
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507
        src_short = szEmpty;

    if (!tgt_long)
        tgt_long = tgt_short;

    if (!src_short) {
        src_short = tgt_short;
        src_long = tgt_long;
    }

    if (!src_long)
        src_long = src_short;

    /* FIXME: use the target short path too */
    folder->TargetDefault = strdupW(tgt_long);
    folder->SourceShortPath = strdupW(src_short);
    folder->SourceLongPath = strdupW(src_long);
    msi_free(p);

    TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
    TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
    TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));

1508 1509
    load_folder_persistence( package, folder );

1510
    list_add_tail( &package->folders, &folder->entry );
1511 1512
    return ERROR_SUCCESS;
}
1513

1514 1515 1516
static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
{
    FolderList *fl;
1517

1518 1519 1520
    if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
    fl->folder = child;
    list_add_tail( &parent->children, &fl->entry );
1521 1522 1523
    return ERROR_SUCCESS;
}

1524 1525 1526 1527 1528
static UINT find_folder_children( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    MSIFOLDER *parent, *child;

1529
    if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1530 1531 1532 1533
        return ERROR_FUNCTION_FAILED;

    if (!child->Parent) return ERROR_SUCCESS;

1534
    if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1535 1536 1537 1538 1539
        return ERROR_FUNCTION_FAILED;

    return add_folder_child( parent, child );
}

1540 1541 1542
static UINT load_all_folders( MSIPACKAGE *package )
{
    static const WCHAR query[] = {
1543 1544
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','D','i','r','e','c','t','o','r','y','`',0};
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554
    MSIQUERY *view;
    UINT r;

    if (!list_empty(&package->folders))
        return ERROR_SUCCESS;

    r = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (r != ERROR_SUCCESS)
        return r;

1555 1556 1557 1558 1559 1560 1561 1562
    r = MSI_IterateRecords( view, NULL, load_folder, package );
    if (r != ERROR_SUCCESS)
    {
        msiobj_release( &view->hdr );
        return r;
    }
    r = MSI_IterateRecords( view, NULL, find_folder_children, package );
    msiobj_release( &view->hdr );
1563 1564
    return r;
}
1565 1566 1567

static UINT ACTION_CostInitialize(MSIPACKAGE *package)
{
1568 1569
    msi_set_property( package->db, szCostingComplete, szZero, -1 );
    msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1570

James Hawkins's avatar
James Hawkins committed
1571
    load_all_folders( package );
1572 1573
    msi_load_all_components( package );
    msi_load_all_features( package );
1574
    load_all_files( package );
1575
    load_all_patches( package );
1576
    load_all_media( package );
1577 1578 1579 1580

    return ERROR_SUCCESS;
}

1581 1582 1583
static UINT execute_script( MSIPACKAGE *package, UINT script )
{
    UINT i, rc = ERROR_SUCCESS;
1584

1585
    TRACE("executing script %u\n", script);
1586

1587 1588
    package->script = script;

1589
    if (script == SCRIPT_ROLLBACK)
1590
    {
1591
        for (i = package->script_actions_count[script]; i > 0; i--)
1592
        {
1593
            rc = ACTION_PerformAction(package, package->script_actions[script][i-1]);
1594 1595 1596 1597 1598 1599
            if (rc != ERROR_SUCCESS)
            {
                ERR("Execution of script %i halted; action %s returned %u\n",
                    script, debugstr_w(package->script_actions[script][i-1]), rc);
                break;
            }
1600 1601 1602 1603
        }
    }
    else
    {
1604
        for (i = 0; i < package->script_actions_count[script]; i++)
1605
        {
1606
            rc = ACTION_PerformAction(package, package->script_actions[script][i]);
1607 1608 1609 1610 1611 1612
            if (rc != ERROR_SUCCESS)
            {
                ERR("Execution of script %i halted; action %s returned %u\n",
                    script, debugstr_w(package->script_actions[script][i]), rc);
                break;
            }
1613
        }
1614
    }
1615 1616 1617

    package->script = SCRIPT_NONE;

1618
    msi_free_action_script(package, script);
1619 1620 1621
    return rc;
}

1622 1623 1624 1625 1626
static UINT ACTION_FileCost(MSIPACKAGE *package)
{
    return ERROR_SUCCESS;
}

1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
static void get_client_counts( MSIPACKAGE *package )
{
    MSICOMPONENT *comp;
    HKEY hkey;

    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
    {
        if (!comp->ComponentId) continue;

        if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
            MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
        {
            comp->num_clients = 0;
            continue;
        }
        RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
                          NULL, NULL, NULL, NULL );
        RegCloseKey( hkey );
    }
}

1648
static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1649
{
1650
    MSICOMPONENT *comp;
1651
    UINT r;
1652

1653 1654
    LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
    {
1655
        if (!comp->ComponentId) continue;
1656

1657 1658 1659
        r = MsiQueryComponentStateW( package->ProductCode, NULL,
                                     MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
                                     &comp->Installed );
1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672
        if (r == ERROR_SUCCESS) continue;

        r = MsiQueryComponentStateW( package->ProductCode, NULL,
                                     MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
                                     &comp->Installed );
        if (r == ERROR_SUCCESS) continue;

        r = MsiQueryComponentStateW( package->ProductCode, NULL,
                                     MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
                                     &comp->Installed );
        if (r == ERROR_SUCCESS) continue;

        comp->Installed = INSTALLSTATE_ABSENT;
1673
    }
1674 1675
}

1676
static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1677 1678
{
    MSIFEATURE *feature;
1679

1680
    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1681
    {
1682 1683 1684
        INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );

        if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1685 1686
            feature->Installed = INSTALLSTATE_ABSENT;
        else
1687
            feature->Installed = state;
1688 1689 1690
    }
}

1691 1692 1693 1694 1695
static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
{
    return (feature->Level > 0 && feature->Level <= level);
}

1696 1697
static BOOL process_state_property(MSIPACKAGE* package, int level,
                                   LPCWSTR property, INSTALLSTATE state)
1698
{
1699
    LPWSTR override;
1700
    MSIFEATURE *feature;
1701 1702
    BOOL remove = !strcmpW(property, szRemove);
    BOOL reinstall = !strcmpW(property, szReinstall);
1703

1704
    override = msi_dup_property( package->db, property );
1705 1706
    if (!override)
        return FALSE;
1707

1708
    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1709
    {
1710
        if (feature->Level <= 0)
1711 1712
            continue;

1713 1714 1715 1716
        if (reinstall)
            state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
        else if (remove)
            state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1717

1718
        if (!strcmpiW( override, szAll ))
1719
        {
1720 1721
            feature->Action = state;
            feature->ActionRequest = state;
1722
        }
1723 1724 1725 1726
        else
        {
            LPWSTR ptr = override;
            LPWSTR ptr2 = strchrW(override,',');
1727

1728 1729
            while (ptr)
            {
1730 1731 1732 1733
                int len = ptr2 - ptr;

                if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
                    || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1734
                {
1735 1736
                    feature->Action = state;
                    feature->ActionRequest = state;
1737
                    break;
1738
                }
1739 1740 1741 1742 1743 1744 1745
                if (ptr2)
                {
                    ptr=ptr2+1;
                    ptr2 = strchrW(ptr,',');
                }
                else
                    break;
1746
            }
1747
        }
1748
    }
1749
    msi_free(override);
1750
    return TRUE;
1751 1752
}

1753
static BOOL process_overrides( MSIPACKAGE *package, int level )
1754
{
1755 1756
    static const WCHAR szAddLocal[] =
        {'A','D','D','L','O','C','A','L',0};
1757 1758
    static const WCHAR szAddSource[] =
        {'A','D','D','S','O','U','R','C','E',0};
1759 1760
    static const WCHAR szAdvertise[] =
        {'A','D','V','E','R','T','I','S','E',0};
1761
    BOOL ret = FALSE;
1762

1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783
    /* all these activation/deactivation things happen in order and things
     * later on the list override things earlier on the list.
     *
     *  0  INSTALLLEVEL processing
     *  1  ADDLOCAL
     *  2  REMOVE
     *  3  ADDSOURCE
     *  4  ADDDEFAULT
     *  5  REINSTALL
     *  6  ADVERTISE
     *  7  COMPADDLOCAL
     *  8  COMPADDSOURCE
     *  9  FILEADDLOCAL
     * 10  FILEADDSOURCE
     * 11  FILEADDDEFAULT
     */
    ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
    ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
    ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
    ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
    ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1784

1785
    if (ret)
1786
        msi_set_property( package->db, szPreselected, szOne, -1 );
1787 1788 1789 1790

    return ret;
}

1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829
static void disable_children( MSIFEATURE *feature, int level )
{
    FeatureList *fl;

    LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
    {
        if (!is_feature_selected( feature, level ))
        {
            TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
                  debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
                  debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);

            fl->feature->Level = feature->Level;
            fl->feature->Action = INSTALLSTATE_UNKNOWN;
            fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
        }
        disable_children( fl->feature, level );
    }
}

static void follow_parent( MSIFEATURE *feature )
{
    FeatureList *fl;

    LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
    {
        if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
        {
            TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
                  debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
                  debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);

            fl->feature->Action = feature->Action;
            fl->feature->ActionRequest = feature->ActionRequest;
        }
        follow_parent( fl->feature );
    }
}

1830 1831 1832 1833 1834
UINT MSI_SetFeatureStates(MSIPACKAGE *package)
{
    int level;
    MSICOMPONENT* component;
    MSIFEATURE *feature;
1835 1836 1837

    TRACE("Checking Install Level\n");

1838
    level = msi_get_property_int(package->db, szInstallLevel, 1);
1839

1840
    if (msi_get_property_int( package->db, szPreselected, 0 ))
1841
    {
1842
        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1843
        {
1844
            if (!is_feature_selected( feature, level )) continue;
1845

1846
            if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1847
            {
1848
                if (feature->Installed == INSTALLSTATE_ABSENT)
1849
                {
1850 1851
                    feature->Action = INSTALLSTATE_UNKNOWN;
                    feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1852
                }
1853
                else
1854
                {
1855 1856
                    feature->Action = feature->Installed;
                    feature->ActionRequest = feature->Installed;
1857
                }
1858
            }
1859
        }
1860 1861
        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
        {
1862 1863 1864
            if (feature->Feature_Parent) continue;
            disable_children( feature, level );
            follow_parent( feature );
1865
        }
1866
    }
1867
    else if (!msi_get_property_int( package->db, szInstalled, 0 ))
1868 1869 1870
    {
        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
        {
1871
            if (!is_feature_selected( feature, level )) continue;
1872

1873
            if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1874
            {
1875
                if (feature->Attributes & msidbFeatureAttributesFavorSource)
1876
                {
1877 1878 1879 1880 1881 1882 1883
                    feature->Action = INSTALLSTATE_SOURCE;
                    feature->ActionRequest = INSTALLSTATE_SOURCE;
                }
                else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
                {
                    feature->Action = INSTALLSTATE_ADVERTISED;
                    feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1884 1885 1886
                }
                else
                {
1887 1888
                    feature->Action = INSTALLSTATE_LOCAL;
                    feature->ActionRequest = INSTALLSTATE_LOCAL;
1889
                }
1890 1891
            }
        }
1892
        /* disable child features of unselected parent or follow parent */
1893 1894
        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
        {
1895 1896 1897
            if (feature->Feature_Parent) continue;
            disable_children( feature, level );
            follow_parent( feature );
1898
        }
1899
    }
1900

1901
    /* now we want to set component state based based on feature state */
1902
    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1903
    {
1904 1905
        ComponentList *cl;

1906
        TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1907 1908
              debugstr_w(feature->Feature), feature->Level, feature->Installed,
              feature->ActionRequest, feature->Action);
1909

1910 1911 1912
        /* features with components that have compressed files are made local */
        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
        {
1913
            if (cl->component->ForceLocalState &&
1914
                feature->ActionRequest == INSTALLSTATE_SOURCE)
1915
            {
1916 1917
                feature->Action = INSTALLSTATE_LOCAL;
                feature->ActionRequest = INSTALLSTATE_LOCAL;
1918 1919 1920
                break;
            }
        }
1921

1922
        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1923
        {
1924
            component = cl->component;
1925

1926
            switch (feature->ActionRequest)
1927
            {
1928 1929 1930
            case INSTALLSTATE_ABSENT:
                component->anyAbsent = 1;
                break;
1931
            case INSTALLSTATE_ADVERTISED:
1932
                component->hasAdvertisedFeature = 1;
1933 1934 1935 1936 1937 1938 1939 1940 1941
                break;
            case INSTALLSTATE_SOURCE:
                component->hasSourceFeature = 1;
                break;
            case INSTALLSTATE_LOCAL:
                component->hasLocalFeature = 1;
                break;
            case INSTALLSTATE_DEFAULT:
                if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1942
                    component->hasAdvertisedFeature = 1;
1943 1944
                else if (feature->Attributes & msidbFeatureAttributesFavorSource)
                    component->hasSourceFeature = 1;
1945
                else
1946 1947
                    component->hasLocalFeature = 1;
                break;
1948 1949 1950 1951 1952 1953 1954 1955
            case INSTALLSTATE_UNKNOWN:
                if (feature->Installed == INSTALLSTATE_ADVERTISED)
                    component->hasAdvertisedFeature = 1;
                if (feature->Installed == INSTALLSTATE_SOURCE)
                    component->hasSourceFeature = 1;
                if (feature->Installed == INSTALLSTATE_LOCAL)
                    component->hasLocalFeature = 1;
                break;
1956 1957
            default:
                break;
1958
            }
1959 1960
        }
    }
1961

1962 1963 1964 1965 1966 1967 1968 1969
    LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
    {
        /* check if it's local or source */
        if (!(component->Attributes & msidbComponentAttributesOptional) &&
             (component->hasLocalFeature || component->hasSourceFeature))
        {
            if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
                 !component->ForceLocalState)
1970 1971 1972 1973
            {
                component->Action = INSTALLSTATE_SOURCE;
                component->ActionRequest = INSTALLSTATE_SOURCE;
            }
1974
            else
1975 1976 1977 1978
            {
                component->Action = INSTALLSTATE_LOCAL;
                component->ActionRequest = INSTALLSTATE_LOCAL;
            }
1979 1980
            continue;
        }
1981

1982 1983 1984
        /* if any feature is local, the component must be local too */
        if (component->hasLocalFeature)
        {
1985 1986
            component->Action = INSTALLSTATE_LOCAL;
            component->ActionRequest = INSTALLSTATE_LOCAL;
1987 1988 1989 1990
            continue;
        }
        if (component->hasSourceFeature)
        {
1991 1992
            component->Action = INSTALLSTATE_SOURCE;
            component->ActionRequest = INSTALLSTATE_SOURCE;
1993
            continue;
1994
        }
1995
        if (component->hasAdvertisedFeature)
1996
        {
1997 1998
            component->Action = INSTALLSTATE_ADVERTISED;
            component->ActionRequest = INSTALLSTATE_ADVERTISED;
1999 2000 2001
            continue;
        }
        TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2002
        if (component->anyAbsent && component->ComponentId)
2003 2004 2005 2006
        {
            component->Action = INSTALLSTATE_ABSENT;
            component->ActionRequest = INSTALLSTATE_ABSENT;
        }
2007
    }
2008

2009
    LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2010
    {
2011
        if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2012 2013
        {
            TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2014 2015 2016 2017 2018 2019 2020 2021 2022 2023
            component->Action = INSTALLSTATE_LOCAL;
            component->ActionRequest = INSTALLSTATE_LOCAL;
        }

        if (component->ActionRequest == INSTALLSTATE_SOURCE &&
            component->Installed == INSTALLSTATE_SOURCE &&
            component->hasSourceFeature)
        {
            component->Action = INSTALLSTATE_UNKNOWN;
            component->ActionRequest = INSTALLSTATE_UNKNOWN;
2024 2025
        }

2026
        TRACE("component %s (installed %d request %d action %d)\n",
2027
              debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2028 2029 2030 2031 2032

        if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
            component->num_clients++;
        else if (component->Action == INSTALLSTATE_ABSENT)
            component->num_clients--;
2033 2034
    }

2035 2036 2037
    return ERROR_SUCCESS;
}

2038 2039
static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
{
2040
    MSIPACKAGE *package = param;
2041 2042
    LPCWSTR name;
    MSIFEATURE *feature;
2043

2044
    name = MSI_RecordGetString( row, 1 );
2045

2046
    feature = msi_get_loaded_feature( package, name );
2047 2048
    if (!feature)
        ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2049 2050 2051 2052 2053
    else
    {
        LPCWSTR Condition;
        Condition = MSI_RecordGetString(row,3);

2054
        if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2055 2056
        {
            int level = MSI_RecordGetInteger(row,2);
2057
            TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2058
            feature->Level = level;
2059 2060 2061 2062 2063
        }
    }
    return ERROR_SUCCESS;
}

2064
VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2065
{
2066
    static const WCHAR name[] = {'\\',0};
2067
    VS_FIXEDFILEINFO *ptr, *ret;
2068
    LPVOID version;
2069
    DWORD versize, handle;
2070 2071 2072 2073 2074 2075 2076
    UINT sz;

    versize = GetFileVersionInfoSizeW( filename, &handle );
    if (!versize)
        return NULL;

    version = msi_alloc( versize );
2077 2078 2079
    if (!version)
        return NULL;

2080 2081
    GetFileVersionInfoW( filename, 0, versize, version );

2082
    if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2083 2084 2085 2086
    {
        msi_free( version );
        return NULL;
    }
2087

2088 2089 2090
    ret = msi_alloc( sz );
    memcpy( ret, ptr, sz );

2091
    msi_free( version );
2092 2093
    return ret;
}
2094

2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105
int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
{
    DWORD ms, ls;

    msi_parse_version_string( version, &ms, &ls );

    if (fi->dwFileVersionMS > ms) return 1;
    else if (fi->dwFileVersionMS < ms) return -1;
    else if (fi->dwFileVersionLS > ls) return 1;
    else if (fi->dwFileVersionLS < ls) return -1;
    return 0;
2106 2107
}

2108
int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119
{
    DWORD ms1, ms2;

    msi_parse_version_string( ver1, &ms1, NULL );
    msi_parse_version_string( ver2, &ms2, NULL );

    if (ms1 > ms2) return 1;
    else if (ms1 < ms2) return -1;
    return 0;
}

2120
DWORD msi_get_disk_file_size( LPCWSTR filename )
2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133
{
    HANDLE file;
    DWORD size;

    file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
    if (file == INVALID_HANDLE_VALUE)
        return INVALID_FILE_SIZE;

    size = GetFileSize( file, NULL );
    CloseHandle( file );
    return size;
}

2134
BOOL msi_file_hash_matches( MSIFILE *file )
2135 2136 2137 2138 2139
{
    UINT r;
    MSIFILEHASHINFO hash;

    hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
Hans Leidekker's avatar
Hans Leidekker committed
2140
    r = msi_get_filehash( file->TargetPath, &hash );
2141 2142 2143 2144 2145 2146
    if (r != ERROR_SUCCESS)
        return FALSE;

    return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
}

2147
static WCHAR *create_temp_dir( MSIDATABASE *db )
2148 2149
{
    static UINT id;
2150
    WCHAR *ret;
2151

2152
    if (!db->tempfolder)
2153
    {
2154
        WCHAR tmp[MAX_PATH];
2155
        UINT len = ARRAY_SIZE( tmp );
2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175

        if (msi_get_property( db, szTempFolder, tmp, &len ) ||
            GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
        {
            GetTempPathW( MAX_PATH, tmp );
        }
        if (!(db->tempfolder = strdupW( tmp ))) return NULL;
    }

    if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
    {
        for (;;)
        {
            if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
            {
                msi_free( ret );
                return NULL;
            }
            if (CreateDirectoryW( ret, NULL )) break;
        }
2176
    }
2177 2178

    return ret;
2179 2180
}

2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222
/*
 *  msi_build_directory_name()
 *
 *  This function is to save messing round with directory names
 *  It handles adding backslashes between path segments,
 *  and can add \ at the end of the directory name if told to.
 *
 *  It takes a variable number of arguments.
 *  It always allocates a new string for the result, so make sure
 *  to free the return value when finished with it.
 *
 *  The first arg is the number of path segments that follow.
 *  The arguments following count are a list of path segments.
 *  A path segment may be NULL.
 *
 *  Path segments will be added with a \ separating them.
 *  A \ will not be added after the last segment, however if the
 *  last segment is NULL, then the last character will be a \
 */
WCHAR *msi_build_directory_name( DWORD count, ... )
{
    DWORD sz = 1, i;
    WCHAR *dir;
    va_list va;

    va_start( va, count );
    for (i = 0; i < count; i++)
    {
        const WCHAR *str = va_arg( va, const WCHAR * );
        if (str) sz += strlenW( str ) + 1;
    }
    va_end( va );

    dir = msi_alloc( sz * sizeof(WCHAR) );
    dir[0] = 0;

    va_start( va, count );
    for (i = 0; i < count; i++)
    {
        const WCHAR *str = va_arg( va, const WCHAR * );
        if (!str) continue;
        strcatW( dir, str );
2223
        if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2224 2225 2226 2227 2228
    }
    va_end( va );
    return dir;
}

2229
BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2230
{
2231 2232
    return comp->assembly && !comp->assembly->application;
}
2233

2234 2235
static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
{
2236
    msi_free( file->TargetPath );
2237
    if (msi_is_global_assembly( file->Component ))
2238
    {
2239 2240
        MSIASSEMBLY *assembly = file->Component->assembly;

2241
        if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2242
        file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2243 2244 2245
    }
    else
    {
2246
        const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2247
        file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2248 2249
    }

Hans Leidekker's avatar
Hans Leidekker committed
2250
    TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2251 2252
}

2253
static UINT calculate_file_cost( MSIPACKAGE *package )
2254
{
2255
    VS_FIXEDFILEINFO *file_version;
2256
    WCHAR *font_version;
2257
    MSIFILE *file;
2258

2259
    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2260
    {
2261
        MSICOMPONENT *comp = file->Component;
2262
        DWORD file_size;
2263

2264
        if (!comp->Enabled) continue;
2265

2266 2267 2268
        if (file->IsCompressed)
            comp->ForceLocalState = TRUE;

2269
        set_target_path( package, file );
2270

2271 2272
        if ((comp->assembly && !comp->assembly->installed) ||
            GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2273 2274 2275 2276
        {
            comp->Cost += file->FileSize;
            continue;
        }
2277
        file_size = msi_get_disk_file_size( file->TargetPath );
Hans Leidekker's avatar
Hans Leidekker committed
2278
        TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2279

2280
        if (file->Version)
2281
        {
2282
            if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2283
            {
2284 2285
                if (msi_compare_file_versions( file_version, file->Version ) < 0)
                {
2286
                    comp->Cost += file->FileSize - file_size;
2287 2288 2289
                }
                msi_free( file_version );
                continue;
2290
            }
2291
            else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2292
            {
2293 2294
                if (msi_compare_font_versions( font_version, file->Version ) < 0)
                {
2295
                    comp->Cost += file->FileSize - file_size;
2296 2297 2298
                }
                msi_free( font_version );
                continue;
2299
            }
2300
        }
2301
        if (file_size != file->FileSize)
2302
        {
2303 2304
            comp->Cost += file->FileSize - file_size;
        }
2305
    }
2306 2307 2308
    return ERROR_SUCCESS;
}

2309
WCHAR *msi_normalize_path( const WCHAR *in )
2310
{
2311 2312 2313
    const WCHAR *p = in;
    WCHAR *q, *ret;
    int n, len = strlenW( in ) + 2;
2314

2315 2316 2317
    if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;

    len = 0;
2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343
    while (1)
    {
        /* copy until the end of the string or a space */
        while (*p != ' ' && (*q = *p))
        {
            p++, len++;
            /* reduce many backslashes to one */
            if (*p != '\\' || *q != '\\')
                q++;
        }

        /* quit at the end of the string */
        if (!*p)
            break;

        /* count the number of spaces */
        n = 0;
        while (p[n] == ' ')
            n++;

        /* if it's leading or trailing space, skip it */
        if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
            p += n;
        else  /* copy n spaces */
            while (n && (*q++ = *p++)) n--;
    }
2344 2345
    while (q - ret > 0 && q[-1] == ' ') q--;
    if (q - ret > 0 && q[-1] != '\\')
2346
    {
2347 2348
        q[0] = '\\';
        q[1] = 0;
2349
    }
2350
    return ret;
2351 2352
}

2353 2354 2355 2356 2357 2358
static WCHAR *get_install_location( MSIPACKAGE *package )
{
    HKEY hkey;
    WCHAR *path;

    if (!package->ProductCode) return NULL;
2359 2360 2361 2362 2363 2364
    if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
    if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
    {
        msi_free( path );
        path = NULL;
    }
2365 2366 2367 2368
    RegCloseKey( hkey );
    return path;
}

2369 2370 2371 2372
void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
{
    FolderList *fl;
    MSIFOLDER *folder, *parent, *child;
2373
    WCHAR *path, *normalized_path;
2374 2375 2376

    TRACE("resolving %s\n", debugstr_w(name));

2377
    if (!(folder = msi_get_loaded_folder( package, name ))) return;
2378

2379
    if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2380
    {
2381 2382
        if (!(path = get_install_location( package )) &&
            (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2383
        {
2384
            path = msi_dup_property( package->db, szRootDrive );
2385 2386 2387 2388
        }
    }
    else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
    {
2389 2390 2391 2392 2393 2394 2395
        if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
        {
            parent = msi_get_loaded_folder( package, folder->Parent );
            path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
        }
        else
            path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2396
    }
2397

2398
    normalized_path = msi_normalize_path( path );
2399
    msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2400 2401
    msi_free( path );

2402
    msi_free( folder->ResolvedTarget );
2403
    folder->ResolvedTarget = normalized_path;
2404 2405 2406 2407 2408 2409 2410 2411 2412

    LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
    {
        child = fl->folder;
        msi_resolve_target_folder( package, child->Directory, load_prop );
    }
    TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
}

2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424
static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
{
    MSICOMPONENT *comp;
    ULONGLONG ret = 0;

    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
    {
        if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
    }
    return ret;
}

2425 2426
static UINT ACTION_CostFinalize(MSIPACKAGE *package)
{
2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438
    static const WCHAR query[] =
        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
         '`','C','o','n','d','i','t','i','o','n','`',0};
    static const WCHAR szOutOfDiskSpace[] =
        {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
    static const WCHAR szPrimaryFolder[] =
        {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
    static const WCHAR szPrimaryVolumePath[] =
        {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
    static const WCHAR szPrimaryVolumeSpaceAvailable[] =
        {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
         'A','v','a','i','l','a','b','l','e',0};
2439 2440 2441 2442 2443 2444
    static const WCHAR szPrimaryVolumeSpaceRequired[] =
        {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
         'R','e','q','u','i','r','e','d',0};
    static const WCHAR szPrimaryVolumeSpaceRemaining[] =
        {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
         'R','e','m','a','i','n','i','n','g',0};
2445 2446
    static const WCHAR szOutOfNoRbDiskSpace[] =
        {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2447
    MSICOMPONENT *comp;
2448
    MSIQUERY *view;
2449
    WCHAR *level, *primary_key, *primary_folder;
2450
    UINT rc;
2451

2452
    TRACE("Building directory properties\n");
2453
    msi_resolve_target_folder( package, szTargetDir, TRUE );
2454

2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465
    TRACE("Evaluating component conditions\n");
    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
    {
        if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
        {
            TRACE("Disabling component %s\n", debugstr_w(comp->Component));
            comp->Enabled = FALSE;
        }
        else
            comp->Enabled = TRUE;
    }
2466
    get_client_counts( package );
2467

2468 2469
    /* read components states from the registry */
    ACTION_GetComponentInstallStates(package);
2470
    ACTION_GetFeatureInstallStates(package);
2471

2472
    if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2473
    {
2474
        TRACE("Evaluating feature conditions\n");
2475

2476
        rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2477
        if (rc == ERROR_SUCCESS)
2478
        {
2479 2480
            rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
            msiobj_release( &view->hdr );
2481 2482
            if (rc != ERROR_SUCCESS)
                return rc;
2483
        }
2484
    }
2485

2486 2487
    TRACE("Calculating file cost\n");
    calculate_file_cost( package );
2488

2489
    msi_set_property( package->db, szCostingComplete, szOne, -1 );
2490
    /* set default run level if not set */
2491
    level = msi_dup_property( package->db, szInstallLevel );
2492
    if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2493
    msi_free(level);
2494

2495 2496
    if ((rc = MSI_SetFeatureStates( package ))) return rc;

2497 2498 2499 2500 2501 2502 2503
    if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
    {
        if ((primary_folder = msi_dup_property( package->db, primary_key )))
        {
            if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
                 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
            {
2504
                static const WCHAR fmtW[] = {'%','l','u',0};
2505
                ULARGE_INTEGER free;
2506 2507
                ULONGLONG required;
                WCHAR buf[21];
2508 2509 2510 2511 2512 2513 2514

                primary_folder[2] = 0;
                if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
                {
                    sprintfW( buf, fmtW, free.QuadPart / 512 );
                    msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
                }
2515 2516 2517 2518 2519 2520
                required = get_volume_space_required( package );
                sprintfW( buf, fmtW, required / 512 );
                msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );

                sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
                msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2521 2522 2523 2524 2525 2526 2527
                msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
            }
            msi_free( primary_folder );
        }
        msi_free( primary_key );
    }

2528
    /* FIXME: check volume disk space */
2529
    msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2530
    msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2531

2532
    return ERROR_SUCCESS;
2533 2534
}

2535
static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2536
{
2537
    BYTE *data;
2538

2539 2540
    if (!value)
    {
2541
        *size = sizeof(WCHAR);
2542
        *type = REG_SZ;
2543
        if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2544 2545
        return data;
    }
2546
    if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2547
    {
2548 2549 2550 2551
        if (value[1]=='x')
        {
            LPWSTR ptr;
            CHAR byte[5];
2552
            LPWSTR deformated = NULL;
2553 2554
            int count;

2555
            deformat_string(package, &value[2], &deformated);
2556 2557

            /* binary value type */
2558 2559 2560 2561 2562 2563 2564
            ptr = deformated;
            *type = REG_BINARY;
            if (strlenW(ptr)%2)
                *size = (strlenW(ptr)/2)+1;
            else
                *size = strlenW(ptr)/2;

2565
            data = msi_alloc(*size);
2566

2567 2568 2569 2570
            byte[0] = '0'; 
            byte[1] = 'x'; 
            byte[4] = 0; 
            count = 0;
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580
            /* if uneven pad with a zero in front */
            if (strlenW(ptr)%2)
            {
                byte[2]= '0';
                byte[3]= *ptr;
                ptr++;
                data[count] = (BYTE)strtol(byte,NULL,0);
                count ++;
                TRACE("Uneven byte count\n");
            }
2581 2582 2583 2584 2585 2586 2587 2588 2589
            while (*ptr)
            {
                byte[2]= *ptr;
                ptr++;
                byte[3]= *ptr;
                ptr++;
                data[count] = (BYTE)strtol(byte,NULL,0);
                count ++;
            }
2590
            msi_free(deformated);
2591

2592
            TRACE("Data %i bytes(%i)\n",*size,count);
2593 2594 2595 2596
        }
        else
        {
            LPWSTR deformated;
2597 2598
            LPWSTR p;
            DWORD d = 0;
2599
            deformat_string(package, &value[1], &deformated);
2600 2601 2602

            *type=REG_DWORD; 
            *size = sizeof(DWORD);
2603
            data = msi_alloc(*size);
2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617
            p = deformated;
            if (*p == '-')
                p++;
            while (*p)
            {
                if ( (*p < '0') || (*p > '9') )
                    break;
                d *= 10;
                d += (*p - '0');
                p++;
            }
            if (deformated[0] == '-')
                d = -d;
            *(LPDWORD)data = d;
2618
            TRACE("DWORD %i\n",*(LPDWORD)data);
2619

2620
            msi_free(deformated);
2621
        }
2622 2623 2624
    }
    else
    {
2625
        const WCHAR *ptr = value;
2626

2627 2628
        *type = REG_SZ;
        if (value[0] == '#')
2629
        {
2630
            ptr++; len--;
2631
            if (value[1] == '%')
2632
            {
2633
                ptr++; len--;
2634
                *type = REG_EXPAND_SZ;
2635
            }
2636
        }
2637
        data = (BYTE *)msi_strdupW( ptr, len );
2638 2639
        if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
        *size = (len + 1) * sizeof(WCHAR);
2640 2641 2642 2643
    }
    return data;
}

2644 2645 2646 2647 2648 2649 2650
static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
{
    const WCHAR *ret;

    switch (root)
    {
    case -1:
2651
        if (msi_get_property_int( package->db, szAllUsers, 0 ))
2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685
        {
            *root_key = HKEY_LOCAL_MACHINE;
            ret = szHLM;
        }
        else
        {
            *root_key = HKEY_CURRENT_USER;
            ret = szHCU;
        }
        break;
    case 0:
        *root_key = HKEY_CLASSES_ROOT;
        ret = szHCR;
        break;
    case 1:
        *root_key = HKEY_CURRENT_USER;
        ret = szHCU;
        break;
    case 2:
        *root_key = HKEY_LOCAL_MACHINE;
        ret = szHLM;
        break;
    case 3:
        *root_key = HKEY_USERS;
        ret = szHU;
        break;
    default:
        ERR("Unknown root %i\n", root);
        return NULL;
    }

    return ret;
}

2686 2687 2688 2689 2690 2691 2692 2693
static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
{
    REGSAM view = 0;
    if (is_wow64 || is_64bit)
        view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
    return view;
}

2694
static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2695 2696 2697 2698 2699
{
    WCHAR *subkey, *p, *q;
    HKEY hkey, ret = NULL;
    LONG res;

2700
    access |= get_registry_view( comp );
2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716

    if (!(subkey = strdupW( path ))) return NULL;
    p = subkey;
    if ((q = strchrW( p, '\\' ))) *q = 0;
    if (create)
        res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
    else
        res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
    if (res)
    {
        TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
        msi_free( subkey );
        return NULL;
    }
    if (q && q[1])
    {
2717
        ret = open_key( comp, hkey, q + 1, create, access );
2718 2719 2720 2721 2722 2723 2724
        RegCloseKey( hkey );
    }
    else ret = hkey;
    msi_free( subkey );
    return ret;
}

2725
static BOOL is_special_entry( const WCHAR *name )
2726
{
2727
     return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2728 2729
}

2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891
static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
{
    const WCHAR *p = str;
    WCHAR **ret;
    int i = 0;

    *count = 0;
    if (!str) return NULL;
    while ((p - str) < len)
    {
        p += strlenW( p ) + 1;
        (*count)++;
    }
    if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
    p = str;
    while ((p - str) < len)
    {
        if (!(ret[i] = strdupW( p )))
        {
            for (; i >= 0; i--) msi_free( ret[i] );
            msi_free( ret );
            return NULL;
        }
        p += strlenW( p ) + 1;
        i++;
    }
    return ret;
}

static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
                                           WCHAR **right, DWORD right_count, DWORD *size )
{
    WCHAR *ret, *p;
    unsigned int i;

    *size = sizeof(WCHAR);
    for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
    for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);

    if (!(ret = p = msi_alloc( *size ))) return NULL;

    for (i = 0; i < left_count; i++)
    {
        strcpyW( p, left[i] );
        p += strlenW( p ) + 1;
    }
    for (i = 0; i < right_count; i++)
    {
        strcpyW( p, right[i] );
        p += strlenW( p ) + 1;
    }
    *p = 0;
    return ret;
}

static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
                                      WCHAR **new, DWORD new_count )
{
    DWORD ret = old_count;
    unsigned int i, j, k;

    for (i = 0; i < new_count; i++)
    {
        for (j = 0; j < old_count; j++)
        {
            if (old[j] && !strcmpW( new[i], old[j] ))
            {
                msi_free( old[j] );
                for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
                old[k] = NULL;
                ret--;
            }
        }
    }
    return ret;
}

enum join_op
{
    JOIN_OP_APPEND,
    JOIN_OP_PREPEND,
    JOIN_OP_REPLACE
};

static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
                                        WCHAR **new, DWORD new_count, DWORD *size )
{
    switch (op)
    {
    case JOIN_OP_APPEND:
        old_count = remove_duplicate_values( old, old_count, new, new_count );
        return flatten_multi_string_values( old, old_count, new, new_count, size );

    case JOIN_OP_PREPEND:
        old_count = remove_duplicate_values( old, old_count, new, new_count );
        return flatten_multi_string_values( new, new_count, old, old_count, size );

    case JOIN_OP_REPLACE:
        return flatten_multi_string_values( new, new_count, NULL, 0, size );

    default:
        ERR("unhandled join op %u\n", op);
        return NULL;
    }
}

static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
                                       BYTE *new_value, DWORD new_size, DWORD *size )
{
    DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
    const WCHAR *new_ptr = NULL, *old_ptr = NULL;
    enum join_op op = JOIN_OP_REPLACE;
    WCHAR **old = NULL, **new = NULL;
    BYTE *ret;

    if (new_size / sizeof(WCHAR) - 1 > 1)
    {
        new_ptr = (const WCHAR *)new_value;
        new_len = new_size / sizeof(WCHAR) - 1;

        if (!new_ptr[0] && new_ptr[new_len - 1])
        {
            op = JOIN_OP_APPEND;
            new_len--;
            new_ptr++;
        }
        else if (new_ptr[0] && !new_ptr[new_len - 1])
        {
            op = JOIN_OP_PREPEND;
            new_len--;
        }
        else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
        {
            op = JOIN_OP_REPLACE;
            new_len -= 2;
            new_ptr++;
        }
        new = split_multi_string_values( new_ptr, new_len, &new_count );
    }
    if (old_size / sizeof(WCHAR) - 1 > 1)
    {
        old_ptr = (const WCHAR *)old_value;
        old_len = old_size / sizeof(WCHAR) - 1;
        old = split_multi_string_values( old_ptr, old_len, &old_count );
    }
    ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
    for (i = 0; i < old_count; i++) msi_free( old[i] );
    for (i = 0; i < new_count; i++) msi_free( new[i] );
    msi_free( old );
    msi_free( new );
    return ret;
}

static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
{
    BYTE *ret;
    if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
    if (!(ret = msi_alloc( *size ))) return NULL;
    RegQueryValueExW( hkey, name, NULL, type, ret, size );
    return ret;
}

2892
static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2893
{
2894
    MSIPACKAGE *package = param;
2895
    BYTE *new_value, *old_value = NULL;
2896
    HKEY  root_key, hkey;
2897
    DWORD type, old_type, new_size, old_size = 0;
2898
    LPWSTR deformated, uikey;
2899
    const WCHAR *szRoot, *component, *name, *key, *str;
2900
    MSICOMPONENT *comp;
2901 2902 2903
    MSIRECORD * uirow;
    INT   root;
    BOOL check_first = FALSE;
2904
    int len;
2905

2906
    msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2907

2908
    component = MSI_RecordGetString(row, 6);
2909
    comp = msi_get_loaded_component(package,component);
2910 2911
    if (!comp)
        return ERROR_SUCCESS;
2912

2913
    comp->Action = msi_get_component_action( package, comp );
2914
    if (comp->Action != INSTALLSTATE_LOCAL && comp->Action != INSTALLSTATE_SOURCE)
2915
    {
2916
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2917 2918
        return ERROR_SUCCESS;
    }
2919

2920 2921 2922 2923 2924 2925
    name = MSI_RecordGetString(row, 4);
    if( MSI_RecordIsNull(row,5) && name )
    {
        /* null values can have special meanings */
        if (name[0]=='-' && name[1] == 0)
                return ERROR_SUCCESS;
2926 2927
        if ((name[0] == '+' || name[0] == '*') && !name[1])
            check_first = TRUE;
2928
    }
2929

2930 2931 2932
    root = MSI_RecordGetInteger(row,2);
    key = MSI_RecordGetString(row, 3);

2933 2934
    szRoot = get_root_key( package, root, &root_key );
    if (!szRoot)
2935
        return ERROR_SUCCESS;
2936

2937
    deformat_string(package, key , &deformated);
2938
    uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2939 2940
    strcpyW(uikey,szRoot);
    strcatW(uikey,deformated);
2941

2942
    if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2943
    {
2944
        ERR("Could not create key %s\n", debugstr_w(deformated));
2945
        msi_free(uikey);
2946
        msi_free(deformated);
2947
        return ERROR_FUNCTION_FAILED;
2948
    }
2949
    msi_free( deformated );
2950 2951 2952 2953 2954
    str = msi_record_get_string( row, 5, NULL );
    len = deformat_string( package, str, &deformated );
    new_value = parse_value( package, deformated, len, &type, &new_size );

    msi_free( deformated );
2955
    deformat_string(package, name, &deformated);
2956 2957

    if (!is_special_entry( name ))
2958
    {
2959 2960 2961 2962
        old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
        if (type == REG_MULTI_SZ)
        {
            BYTE *new;
2963
            if (old_value && old_type != REG_MULTI_SZ)
2964 2965 2966 2967 2968 2969 2970 2971 2972
            {
                msi_free( old_value );
                old_value = NULL;
                old_size = 0;
            }
            new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
            msi_free( new_value );
            new_value = new;
        }
2973
        if (!check_first)
Aric Stewart's avatar
Aric Stewart committed
2974
        {
2975 2976
            TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
            RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
Aric Stewart's avatar
Aric Stewart committed
2977
        }
2978
        else if (!old_value)
Aric Stewart's avatar
Aric Stewart committed
2979
        {
2980
            if (deformated || new_size)
2981
            {
2982 2983
                TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
                RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2984
            }
Aric Stewart's avatar
Aric Stewart committed
2985
        }
2986
        else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2987 2988
    }
    RegCloseKey(hkey);
2989

2990 2991 2992
    uirow = MSI_CreateRecord(3);
    MSI_RecordSetStringW(uirow,2,deformated);
    MSI_RecordSetStringW(uirow,1,uikey);
2993
    if (type == REG_SZ || type == REG_EXPAND_SZ)
2994
        MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2995
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2996
    msiobj_release( &uirow->hdr );
2997

2998 2999
    msi_free(new_value);
    msi_free(old_value);
3000 3001
    msi_free(deformated);
    msi_free(uikey);
3002 3003 3004 3005 3006 3007

    return ERROR_SUCCESS;
}

static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
{
3008 3009 3010 3011
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','R','e','g','i','s','t','r','y','`',0};
    MSIQUERY *view;
3012 3013
    UINT rc;

3014 3015 3016
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szWriteRegistryValues);

3017
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3018 3019 3020 3021
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
3022
    msiobj_release(&view->hdr);
3023 3024 3025
    return rc;
}

3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040
static int is_key_empty(const MSICOMPONENT *comp, HKEY root, const WCHAR *path)
{
    DWORD subkeys, values;
    HKEY key;
    LONG res;

    key = open_key(comp, root, path, FALSE, get_registry_view(comp) | KEY_READ);
    if (!key) return 0;

    res = RegQueryInfoKeyW(key, 0, 0, 0, &subkeys, 0, 0, &values, 0, 0, 0, 0);
    RegCloseKey(key);

    return !res && !subkeys && !values;
}

3041
static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3042
{
3043
    LONG res = ERROR_SUCCESS;
3044 3045 3046 3047
    REGSAM access = 0;
    WCHAR *subkey, *p;
    HKEY hkey;

3048
    access |= get_registry_view( comp );
3049 3050

    if (!(subkey = strdupW( path ))) return;
3051
    do
3052
    {
3053 3054 3055 3056
        if ((p = strrchrW( subkey, '\\' )))
        {
            *p = 0;
            if (!p[1]) continue; /* trailing backslash */
3057
            hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3058
            if (!hkey) break;
3059 3060 3061 3062 3063
            if (!is_key_empty(comp, hkey, p + 1))
            {
                RegCloseKey(hkey);
                break;
            }
3064
            res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3065 3066
            RegCloseKey( hkey );
        }
3067
        else if (is_key_empty(comp, root, subkey))
3068 3069 3070 3071 3072 3073
            res = RegDeleteKeyExW( root, subkey, access, 0 );
        if (res)
        {
            TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
            break;
        }
3074
    } while (p);
3075 3076 3077
    msi_free( subkey );
}

3078
static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3079 3080 3081 3082
{
    LONG res;
    HKEY hkey;

3083
    if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3084 3085
    {
        if ((res = RegDeleteValueW( hkey, value )))
3086
            TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3087

3088
        RegCloseKey( hkey );
3089
        if (is_key_empty(comp, root, path))
3090
        {
3091
            TRACE("removing empty key %s\n", debugstr_w(path));
3092
            delete_key( comp, root, path );
3093 3094 3095 3096
        }
    }
}

3097
static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3098
{
3099
    LONG res;
3100
    HKEY hkey;
3101

3102
    if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3103
    res = RegDeleteTreeW( hkey, NULL );
3104
    if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3105
    delete_key( comp, root, path );
3106
    RegCloseKey( hkey );
3107
}
3108 3109 3110 3111 3112

static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPCWSTR component, name, key_str, root_key_str;
3113
    LPWSTR deformated_key, deformated_name, ui_key_str;
3114 3115 3116 3117 3118 3119 3120
    MSICOMPONENT *comp;
    MSIRECORD *uirow;
    BOOL delete_key = FALSE;
    HKEY hkey_root;
    UINT size;
    INT root;

3121
    msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3122 3123

    component = MSI_RecordGetString( row, 6 );
3124
    comp = msi_get_loaded_component( package, component );
3125 3126 3127
    if (!comp)
        return ERROR_SUCCESS;

3128 3129
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
3130
    {
3131
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3132 3133 3134 3135 3136 3137 3138 3139
        return ERROR_SUCCESS;
    }

    name = MSI_RecordGetString( row, 4 );
    if (MSI_RecordIsNull( row, 5 ) && name )
    {
        if (name[0] == '+' && !name[1])
            return ERROR_SUCCESS;
3140
        if ((name[0] == '-' || name[0] == '*') && !name[1])
3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161
        {
            delete_key = TRUE;
            name = NULL;
        }
    }

    root = MSI_RecordGetInteger( row, 2 );
    key_str = MSI_RecordGetString( row, 3 );

    root_key_str = get_root_key( package, root, &hkey_root );
    if (!root_key_str)
        return ERROR_SUCCESS;

    deformat_string( package, key_str, &deformated_key );
    size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
    ui_key_str = msi_alloc( size * sizeof(WCHAR) );
    strcpyW( ui_key_str, root_key_str );
    strcatW( ui_key_str, deformated_key );

    deformat_string( package, name, &deformated_name );

3162 3163
    if (delete_key) delete_tree( comp, hkey_root, deformated_key );
    else delete_value( comp, hkey_root, deformated_key, deformated_name );
3164 3165 3166 3167 3168
    msi_free( deformated_key );

    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, ui_key_str );
    MSI_RecordSetStringW( uirow, 2, deformated_name );
3169
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180
    msiobj_release( &uirow->hdr );

    msi_free( ui_key_str );
    msi_free( deformated_name );
    return ERROR_SUCCESS;
}

static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPCWSTR component, name, key_str, root_key_str;
3181
    LPWSTR deformated_key, deformated_name, ui_key_str;
3182 3183 3184 3185 3186 3187 3188 3189
    MSICOMPONENT *comp;
    MSIRECORD *uirow;
    BOOL delete_key = FALSE;
    HKEY hkey_root;
    UINT size;
    INT root;

    component = MSI_RecordGetString( row, 5 );
3190
    comp = msi_get_loaded_component( package, component );
3191 3192 3193
    if (!comp)
        return ERROR_SUCCESS;

3194 3195
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
3196
    {
3197
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224
        return ERROR_SUCCESS;
    }

    if ((name = MSI_RecordGetString( row, 4 )))
    {
        if (name[0] == '-' && !name[1])
        {
            delete_key = TRUE;
            name = NULL;
        }
    }

    root = MSI_RecordGetInteger( row, 2 );
    key_str = MSI_RecordGetString( row, 3 );

    root_key_str = get_root_key( package, root, &hkey_root );
    if (!root_key_str)
        return ERROR_SUCCESS;

    deformat_string( package, key_str, &deformated_key );
    size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
    ui_key_str = msi_alloc( size * sizeof(WCHAR) );
    strcpyW( ui_key_str, root_key_str );
    strcatW( ui_key_str, deformated_key );

    deformat_string( package, name, &deformated_name );

3225 3226
    if (delete_key) delete_tree( comp, hkey_root, deformated_key );
    else delete_value( comp, hkey_root, deformated_key, deformated_name );
3227 3228 3229 3230 3231
    msi_free( deformated_key );

    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, ui_key_str );
    MSI_RecordSetStringW( uirow, 2, deformated_name );
3232
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3233 3234 3235 3236 3237 3238 3239 3240 3241
    msiobj_release( &uirow->hdr );

    msi_free( ui_key_str );
    msi_free( deformated_name );
    return ERROR_SUCCESS;
}

static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
{
3242 3243 3244 3245 3246 3247
    static const WCHAR registry_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','R','e','g','i','s','t','r','y','`',0};
    static const WCHAR remove_registry_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3248
    MSIQUERY *view;
3249
    UINT rc;
3250

3251 3252 3253
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveRegistryValues);

3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272
    rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
        msiobj_release( &view->hdr );
        if (rc != ERROR_SUCCESS)
            return rc;
    }
    rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
        msiobj_release( &view->hdr );
        if (rc != ERROR_SUCCESS)
            return rc;
    }
    return ERROR_SUCCESS;
}

3273
static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3274 3275 3276 3277
{
    return ERROR_SUCCESS;
}

3278

3279
static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3280
{
3281 3282 3283
    static const WCHAR query[]= {
        'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
        '`','R','e','g','i','s','t','r','y','`',0};
3284
    MSICOMPONENT *comp;
3285
    DWORD total = 0, count = 0;
3286
    MSIQUERY *view;
3287
    MSIFEATURE *feature;
3288
    MSIFILE *file;
3289
    UINT rc;
3290

3291
    TRACE("InstallValidate\n");
3292

3293
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3294
    if (rc == ERROR_SUCCESS)
3295
    {
3296
        rc = MSI_IterateRecords( view, &count, NULL, package );
3297
        msiobj_release( &view->hdr );
3298 3299
        if (rc != ERROR_SUCCESS)
            return rc;
3300
        total += count * REG_PROGRESS_VALUE;
3301
    }
3302 3303
    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
        total += COMPONENT_PROGRESS_VALUE;
3304

3305 3306
    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
        total += file->FileSize;
3307

3308
    msi_ui_progress( package, 0, total, 0, 0 );
3309

3310
    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3311
    {
3312 3313 3314
        TRACE("Feature: %s Installed %d Request %d Action %d\n",
              debugstr_w(feature->Feature), feature->Installed,
              feature->ActionRequest, feature->Action);
3315
    }
3316 3317 3318
    return ERROR_SUCCESS;
}

3319 3320
static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
{
3321
    MSIPACKAGE* package = param;
3322 3323
    LPCWSTR cond = NULL; 
    LPCWSTR message = NULL;
3324 3325
    UINT r;

3326 3327 3328 3329 3330
    static const WCHAR title[]=
        {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};

    cond = MSI_RecordGetString(row,1);

3331 3332
    r = MSI_EvaluateConditionW(package,cond);
    if (r == MSICONDITION_FALSE)
3333
    {
3334
        if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3335 3336 3337 3338 3339 3340 3341 3342 3343
        {
            LPWSTR deformated;
            message = MSI_RecordGetString(row,2);
            deformat_string(package,message,&deformated);
            MessageBoxW(NULL,deformated,title,MB_OK);
            msi_free(deformated);
        }

        return ERROR_INSTALL_FAILURE;
3344 3345 3346 3347 3348
    }

    return ERROR_SUCCESS;
}

3349
static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3350
{
3351 3352 3353 3354
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
    MSIQUERY *view;
3355 3356 3357 3358
    UINT rc;

    TRACE("Checking launch conditions\n");

3359
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3360
    if (rc != ERROR_SUCCESS)
3361
        return ERROR_SUCCESS;
3362

3363
    rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3364
    msiobj_release(&view->hdr);
3365 3366
    return rc;
}
3367

3368
static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3369 3370
{

3371
    if (!cmp->KeyPath)
3372
        return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3373

3374
    if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3375
    {
3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387
        static const WCHAR query[] = {
            'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
            '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
            '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
        static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
        static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
        MSIRECORD *row;
        UINT root, len;
        LPWSTR deformated, buffer, deformated_name;
        LPCWSTR key, name;

        row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3388
        if (!row)
3389 3390 3391
            return NULL;

        root = MSI_RecordGetInteger(row,2);
3392 3393
        key = MSI_RecordGetString(row, 3);
        name = MSI_RecordGetString(row, 4);
3394 3395 3396
        deformat_string(package, key , &deformated);
        deformat_string(package, name, &deformated_name);

3397
        len = strlenW(deformated) + 6;
3398 3399 3400
        if (deformated_name)
            len+=strlenW(deformated_name);

3401
        buffer = msi_alloc( len *sizeof(WCHAR));
3402 3403 3404 3405 3406 3407

        if (deformated_name)
            sprintfW(buffer,fmt2,root,deformated,deformated_name);
        else
            sprintfW(buffer,fmt,root,deformated);

3408 3409
        msi_free(deformated);
        msi_free(deformated_name);
3410 3411 3412 3413
        msiobj_release(&row->hdr);

        return buffer;
    }
3414
    else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3415 3416
    {
        FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3417
        return NULL;
3418 3419 3420
    }
    else
    {
3421
        MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3422

3423 3424
        if (file)
            return strdupW( file->TargetPath );
3425
    }
3426
    return NULL;
3427 3428
}

3429
static HKEY openSharedDLLsKey(void)
3430 3431
{
    HKEY hkey=0;
3432 3433 3434 3435 3436 3437
    static const WCHAR path[] =
        {'S','o','f','t','w','a','r','e','\\',
         'M','i','c','r','o','s','o','f','t','\\',
         'W','i','n','d','o','w','s','\\',
         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
         'S','h','a','r','e','d','D','L','L','s',0};
3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464

    RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
    return hkey;
}

static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
{
    HKEY hkey;
    DWORD count=0;
    DWORD type;
    DWORD sz = sizeof(count);
    DWORD rc;
    
    hkey = openSharedDLLsKey();
    rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
    if (rc != ERROR_SUCCESS)
        count = 0;
    RegCloseKey(hkey);
    return count;
}

static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
{
    HKEY hkey;

    hkey = openSharedDLLsKey();
    if (count > 0)
3465
        msi_reg_set_val_dword( hkey, path, count );
3466 3467 3468 3469 3470 3471
    else
        RegDeleteValueW(hkey,path);
    RegCloseKey(hkey);
    return count;
}

3472
static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3473
{
3474
    MSIFEATURE *feature;
3475 3476 3477 3478
    INT count = 0;
    BOOL write = FALSE;

    /* only refcount DLLs */
3479
    if (comp->KeyPath == NULL || 
3480
        comp->assembly ||
3481 3482
        comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
        comp->Attributes & msidbComponentAttributesODBCDataSource)
3483 3484 3485
        write = FALSE;
    else
    {
3486
        count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3487 3488
        write = (count > 0);

3489
        if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3490 3491 3492 3493
            write = TRUE;
    }

    /* increment counts */
3494
    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3495
    {
3496
        ComponentList *cl;
3497

3498
        if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3499 3500
            continue;

3501
        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3502
        {
3503
            if ( cl->component == comp )
3504 3505 3506
                count++;
        }
    }
3507

3508
    /* decrement counts */
3509
    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3510
    {
3511 3512
        ComponentList *cl;

3513
        if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3514 3515
            continue;

3516
        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3517
        {
3518
            if ( cl->component == comp )
3519 3520 3521 3522 3523 3524
                count--;
        }
    }

    /* ref count all the files in the component */
    if (write)
3525 3526 3527 3528
    {
        MSIFILE *file;

        LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3529
        {
3530 3531
            if (file->Component == comp)
                ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3532
        }
3533
    }
3534
    
Austin English's avatar
Austin English committed
3535
    /* add a count for permanent */
3536
    if (comp->Attributes & msidbComponentAttributesPermanent)
3537 3538
        count ++;
    
3539
    comp->RefCount = count;
3540 3541

    if (write)
3542
        ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3543 3544
}

3545 3546 3547 3548
static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
{
    if (comp->assembly)
    {
3549
        static const WCHAR prefixW[] = {'<','\\',0};
3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562
        DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
        WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );

        if (keypath)
        {
            strcpyW( keypath, prefixW );
            strcatW( keypath, comp->assembly->display_name );
        }
        return keypath;
    }
    return resolve_keypath( package, comp );
}

3563
static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3564
{
3565
    WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3566
    UINT rc;
3567
    MSICOMPONENT *comp;
3568
    HKEY hkey;
3569

3570 3571
    TRACE("\n");

3572 3573
    msi_set_sourcedir_props(package, FALSE);

3574 3575 3576 3577 3578
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szProcessComponents);

    squash_guid( package->ProductCode, squashed_pc );

3579
    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3580
    {
3581 3582
        MSIRECORD *uirow;
        INSTALLSTATE action;
3583

3584
        msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3585 3586
        if (!comp->ComponentId)
            continue;
3587

3588
        squash_guid( comp->ComponentId, squashed_cc );
3589 3590
        msi_free( comp->FullKeypath );
        comp->FullKeypath = build_full_keypath( package, comp );
3591

3592
        ACTION_RefCountComponent( package, comp );
3593

3594 3595 3596
        if (package->need_rollback) action = comp->Installed;
        else action = comp->ActionRequest;

3597
        TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3598
                            debugstr_w(comp->Component), debugstr_w(squashed_cc),
3599
                            debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3600 3601

        if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3602
        {
3603
            if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3604
                rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3605
            else
3606
                rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3607

3608 3609
            if (rc != ERROR_SUCCESS)
                continue;
3610

3611
            if (comp->Attributes & msidbComponentAttributesPermanent)
3612
            {
3613 3614 3615 3616
                static const WCHAR szPermKey[] =
                    { '0','0','0','0','0','0','0','0','0','0','0','0',
                      '0','0','0','0','0','0','0','0','0','0','0','0',
                      '0','0','0','0','0','0','0','0',0 };
3617

3618
                msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3619
            }
3620
            if (action == INSTALLSTATE_LOCAL)
3621
                msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3622 3623 3624 3625 3626 3627 3628
            else
            {
                MSIFILE *file;
                MSIRECORD *row;
                LPWSTR ptr, ptr2;
                WCHAR source[MAX_PATH];
                WCHAR base[MAX_PATH];
3629
                LPWSTR sourcepath;
3630 3631 3632 3633 3634 3635 3636 3637 3638

                static const WCHAR fmt[] = {'%','0','2','d','\\',0};
                static const WCHAR query[] = {
                    'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
                    '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
                    '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
                    '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
                    '`','D','i','s','k','I','d','`',0};

3639
                if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3640 3641
                    continue;

3642 3643 3644
                if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
                    return ERROR_FUNCTION_FAILED;

3645 3646 3647 3648 3649 3650 3651 3652
                sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
                ptr2 = strrchrW(source, '\\') + 1;
                msiobj_release(&row->hdr);

                lstrcpyW(base, package->PackagePath);
                ptr = strrchrW(base, '\\');
                *(ptr + 1) = '\0';

3653
                sourcepath = msi_resolve_file_source(package, file);
3654
                ptr = sourcepath + lstrlenW(base);
3655
                lstrcpyW(ptr2, ptr);
3656
                msi_free(sourcepath);
3657

3658
                msi_reg_set_val_str( hkey, squashed_pc, source );
3659
            }
3660
            RegCloseKey(hkey);
3661
        }
3662
        else if (action == INSTALLSTATE_ABSENT)
3663
        {
3664 3665 3666
            if (comp->num_clients <= 0)
            {
                if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3667
                    rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3668
                else
3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686
                    rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );

                if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
            }
            else
            {
                LONG res;

                if (package->Context == MSIINSTALLCONTEXT_MACHINE)
                    rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
                else
                    rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );

                if (rc != ERROR_SUCCESS)
                {
                    WARN( "failed to open component key %u\n", rc );
                    continue;
                }
3687
                res = RegDeleteValueW( hkey, squashed_pc );
3688 3689
                RegCloseKey(hkey);
                if (res) WARN( "failed to delete component value %d\n", res );
3690
            }
3691
        }
3692 3693 3694 3695 3696 3697

        /* UI stuff */
        uirow = MSI_CreateRecord(3);
        MSI_RecordSetStringW(uirow,1,package->ProductCode);
        MSI_RecordSetStringW(uirow,2,comp->ComponentId);
        MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3698
        MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3699 3700
        msiobj_release( &uirow->hdr );
    }
3701
    return ERROR_SUCCESS;
3702 3703
}

3704 3705 3706 3707 3708 3709 3710 3711
typedef struct {
    CLSID       clsid;
    LPWSTR      source;

    LPWSTR      path;
    ITypeLib    *ptLib;
} typelib_struct;

3712
static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729
                                       LPWSTR lpszName, LONG_PTR lParam)
{
    TLIBATTR *attr;
    typelib_struct *tl_struct = (typelib_struct*) lParam;
    static const WCHAR fmt[] = {'%','s','\\','%','i',0};
    int sz; 
    HRESULT res;

    if (!IS_INTRESOURCE(lpszName))
    {
        ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
        return TRUE;
    }

    sz = strlenW(tl_struct->source)+4;
    sz *= sizeof(WCHAR);

3730
    if ((INT_PTR)lpszName == 1)
3731 3732 3733
        tl_struct->path = strdupW(tl_struct->source);
    else
    {
3734
        tl_struct->path = msi_alloc(sz);
3735 3736
        sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
    }
3737 3738 3739

    TRACE("trying %s\n", debugstr_w(tl_struct->path));
    res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3740
    if (FAILED(res))
3741
    {
3742
        msi_free(tl_struct->path);
3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754
        tl_struct->path = NULL;

        return TRUE;
    }

    ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
    if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
    {
        ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
        return FALSE;
    }

3755
    msi_free(tl_struct->path);
3756 3757 3758 3759 3760 3761 3762 3763
    tl_struct->path = NULL;

    ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
    ITypeLib_Release(tl_struct->ptLib);

    return TRUE;
}

3764
static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3765
{
3766
    MSIPACKAGE* package = param;
3767
    LPCWSTR component;
3768
    MSICOMPONENT *comp;
3769
    MSIFILE *file;
3770
    typelib_struct tl_struct;
3771
    ITypeLib *tlib;
3772
    HMODULE module;
3773 3774
    HRESULT hr;

3775
    component = MSI_RecordGetString(row,3);
3776
    comp = msi_get_loaded_component(package,component);
3777
    if (!comp)
3778
        return ERROR_SUCCESS;
3779

3780 3781
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
3782
    {
3783
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3784 3785
        return ERROR_SUCCESS;
    }
3786

3787
    if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3788 3789
    {
        TRACE("component has no key path\n");
3790
        return ERROR_SUCCESS;
3791
    }
3792
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3793

3794
    module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3795
    if (module)
3796
    {
3797 3798
        LPCWSTR guid;
        guid = MSI_RecordGetString(row,1);
3799
        CLSIDFromString( guid, &tl_struct.clsid);
3800
        tl_struct.source = strdupW( file->TargetPath );
3801
        tl_struct.path = NULL;
3802

3803 3804
        EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
                        (LONG_PTR)&tl_struct);
3805

3806
        if (tl_struct.path)
3807
        {
3808
            LPCWSTR helpid, help_path = NULL;
3809
            HRESULT res;
3810

3811
            helpid = MSI_RecordGetString(row,6);
3812

3813 3814
            if (helpid) help_path = msi_get_target_folder( package, helpid );
            res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3815

3816
            if (FAILED(res))
3817
                ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3818 3819 3820 3821
            else
                TRACE("Registered %s\n", debugstr_w(tl_struct.path));

            ITypeLib_Release(tl_struct.ptLib);
3822
            msi_free(tl_struct.path);
3823
        }
3824
        else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3825 3826

        FreeLibrary(module);
3827
        msi_free(tl_struct.source);
3828
    }
3829
    else
3830 3831 3832 3833 3834
    {
        hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
        if (FAILED(hr))
        {
            ERR("Failed to load type library: %08x\n", hr);
3835
            return ERROR_INSTALL_FAILURE;
3836 3837 3838 3839
        }

        ITypeLib_Release(tlib);
    }
3840 3841 3842 3843 3844 3845

    return ERROR_SUCCESS;
}

static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
{
3846 3847 3848 3849
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','T','y','p','e','L','i','b','`',0};
    MSIQUERY *view;
3850 3851
    UINT rc;

3852 3853 3854
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterTypeLibraries);

3855
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3856 3857 3858 3859
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3860
    msiobj_release(&view->hdr);
3861 3862 3863
    return rc;
}

3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875
static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPCWSTR component, guid;
    MSICOMPONENT *comp;
    GUID libid;
    UINT version;
    LCID language;
    SYSKIND syskind;
    HRESULT hr;

    component = MSI_RecordGetString( row, 3 );
3876
    comp = msi_get_loaded_component( package, component );
3877 3878 3879
    if (!comp)
        return ERROR_SUCCESS;

3880 3881
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
3882
    {
3883
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3884 3885
        return ERROR_SUCCESS;
    }
3886
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3887

3888
    guid = MSI_RecordGetString( row, 1 );
3889
    CLSIDFromString( guid, &libid );
3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909
    version = MSI_RecordGetInteger( row, 4 );
    language = MSI_RecordGetInteger( row, 2 );

#ifdef _WIN64
    syskind = SYS_WIN64;
#else
    syskind = SYS_WIN32;
#endif

    hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
    if (FAILED(hr))
    {
        WARN("Failed to unregister typelib: %08x\n", hr);
    }

    return ERROR_SUCCESS;
}

static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
{
3910 3911 3912
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','T','y','p','e','L','i','b','`',0};
3913
    MSIQUERY *view;
3914
    UINT rc;
3915

3916 3917 3918
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szUnregisterTypeLibraries);

3919 3920 3921 3922 3923 3924 3925 3926 3927
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
    msiobj_release( &view->hdr );
    return rc;
}

3928 3929 3930
static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
{
    static const WCHAR szlnk[] = {'.','l','n','k',0};
3931 3932
    LPCWSTR directory, extension, link_folder;
    LPWSTR link_file, filename;
3933 3934

    directory = MSI_RecordGetString( row, 2 );
3935
    link_folder = msi_get_target_folder( package, directory );
3936 3937
    if (!link_folder)
    {
3938 3939
        ERR("unable to resolve folder %s\n", debugstr_w(directory));
        return NULL;
3940
    }
3941
    /* may be needed because of a bug somewhere else */
3942
    msi_create_full_path( link_folder );
3943 3944

    filename = msi_dup_record_field( row, 3 );
3945
    msi_reduce_to_long_filename( filename );
3946

3947
    extension = strrchrW( filename, '.' );
3948 3949 3950 3951 3952 3953
    if (!extension || strcmpiW( extension, szlnk ))
    {
        int len = strlenW( filename );
        filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
        memcpy( filename + len, szlnk, sizeof(szlnk) );
    }
3954
    link_file = msi_build_directory_name( 2, link_folder, filename );
3955 3956 3957 3958 3959
    msi_free( filename );

    return link_file;
}

3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981
WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
{
    static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
    static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
    WCHAR *folder, *dest, *path;

    if (package->Context == MSIINSTALLCONTEXT_MACHINE)
        folder = msi_dup_property( package->db, szWindowsFolder );
    else
    {
        WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
        folder = msi_build_directory_name( 2, appdata, szMicrosoft );
        msi_free( appdata );
    }
    dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
    msi_create_full_path( dest );
    path = msi_build_directory_name( 2, dest, icon_name );
    msi_free( folder );
    msi_free( dest );
    return path;
}

3982
static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3983
{
3984
    MSIPACKAGE *package = param;
3985 3986
    LPWSTR link_file, deformated, path;
    LPCWSTR component, target;
3987
    MSICOMPONENT *comp;
3988 3989
    IShellLinkW *sl = NULL;
    IPersistFile *pf = NULL;
3990 3991
    HRESULT res;

3992
    component = MSI_RecordGetString(row, 4);
3993
    comp = msi_get_loaded_component(package, component);
3994
    if (!comp)
3995
        return ERROR_SUCCESS;
3996

3997 3998
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
3999
    {
4000
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4001 4002
        return ERROR_SUCCESS;
    }
4003
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4004

4005 4006
    res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
                    &IID_IShellLinkW, (LPVOID *) &sl );
4007

4008
    if (FAILED( res ))
4009
    {
4010 4011
        ERR("CLSID_ShellLink not available\n");
        goto err;
4012
    }
4013

4014
    res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
4015
    if (FAILED( res ))
4016
    {
4017 4018
        ERR("QueryInterface(IID_IPersistFile) failed\n");
        goto err;
4019
    }
4020

4021 4022
    target = MSI_RecordGetString(row, 5);
    if (strchrW(target, '['))
4023
    {
4024
        deformat_string( package, target, &path );
4025 4026 4027
        TRACE("target path is %s\n", debugstr_w(path));
        IShellLinkW_SetPath( sl, path );
        msi_free( path );
4028 4029 4030 4031
    }
    else
    {
        FIXME("poorly handled shortcut format, advertised shortcut\n");
4032 4033 4034
        path = resolve_keypath( package, comp );
        IShellLinkW_SetPath( sl, path );
        msi_free( path );
4035
    }
4036

4037 4038
    if (!MSI_RecordIsNull(row,6))
    {
4039 4040
        LPCWSTR arguments = MSI_RecordGetString(row, 6);
        deformat_string(package, arguments, &deformated);
4041
        IShellLinkW_SetArguments(sl,deformated);
4042
        msi_free(deformated);
4043
    }
4044

4045 4046
    if (!MSI_RecordIsNull(row,7))
    {
4047 4048
        LPCWSTR description = MSI_RecordGetString(row, 7);
        IShellLinkW_SetDescription(sl, description);
4049
    }
4050

4051 4052
    if (!MSI_RecordIsNull(row,8))
        IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
4053

4054 4055 4056
    if (!MSI_RecordIsNull(row,9))
    {
        INT index; 
4057
        LPCWSTR icon = MSI_RecordGetString(row, 9);
4058

4059
        path = msi_build_icon_path(package, icon);
4060
        index = MSI_RecordGetInteger(row,10);
4061

4062 4063 4064 4065
        /* no value means 0 */
        if (index == MSI_NULL_INTEGER)
            index = 0;

4066 4067
        IShellLinkW_SetIconLocation(sl, path, index);
        msi_free(path);
4068
    }
4069

4070 4071
    if (!MSI_RecordIsNull(row,11))
        IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4072

4073 4074
    if (!MSI_RecordIsNull(row,12))
    {
4075 4076 4077
        LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
        full_path = msi_get_target_folder( package, wkdir );
        if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4078
    }
4079
    link_file = get_link_file(package, row);
4080

4081 4082 4083
    TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
    IPersistFile_Save(pf, link_file, FALSE);
    msi_free(link_file);
4084

4085 4086 4087 4088 4089
err:
    if (pf)
        IPersistFile_Release( pf );
    if (sl)
        IShellLinkW_Release( sl );
4090

4091 4092
    return ERROR_SUCCESS;
}
4093

4094 4095
static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
{
4096 4097 4098 4099
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','S','h','o','r','t','c','u','t','`',0};
    MSIQUERY *view;
4100
    HRESULT res;
4101
    UINT rc;
4102

4103 4104 4105
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szCreateShortcuts);

4106
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4107 4108 4109 4110
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    res = CoInitialize( NULL );
4111

4112 4113
    rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
    msiobj_release(&view->hdr);
4114

4115
    if (SUCCEEDED(res)) CoUninitialize();
4116 4117 4118
    return rc;
}

4119 4120 4121 4122 4123 4124 4125 4126
static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPWSTR link_file;
    LPCWSTR component;
    MSICOMPONENT *comp;

    component = MSI_RecordGetString( row, 4 );
4127
    comp = msi_get_loaded_component( package, component );
4128 4129 4130
    if (!comp)
        return ERROR_SUCCESS;

4131 4132
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
4133
    {
4134
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4135 4136
        return ERROR_SUCCESS;
    }
4137
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152

    link_file = get_link_file( package, row );

    TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
    if (!DeleteFileW( link_file ))
    {
        WARN("Failed to remove shortcut file %u\n", GetLastError());
    }
    msi_free( link_file );

    return ERROR_SUCCESS;
}

static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
{
4153 4154 4155
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','S','h','o','r','t','c','u','t','`',0};
4156
    MSIQUERY *view;
4157
    UINT rc;
4158

4159 4160 4161
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveShortcuts);

4162 4163 4164 4165 4166 4167 4168 4169 4170
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
    msiobj_release( &view->hdr );
    return rc;
}

4171
static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4172
{
4173
    MSIPACKAGE* package = param;
4174
    HANDLE the_file;
4175 4176
    LPWSTR FilePath;
    LPCWSTR FileName;
4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187
    CHAR buffer[1024];
    DWORD sz;
    UINT rc;

    FileName = MSI_RecordGetString(row,1);
    if (!FileName)
    {
        ERR("Unable to get FileName\n");
        return ERROR_SUCCESS;
    }

4188
    FilePath = msi_build_icon_path(package, FileName);
4189 4190 4191 4192 4193 4194 4195 4196 4197

    TRACE("Creating icon file at %s\n",debugstr_w(FilePath));

    the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL, NULL);

    if (the_file == INVALID_HANDLE_VALUE)
    {
        ERR("Unable to create file %s\n",debugstr_w(FilePath));
4198
        msi_free(FilePath);
4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215
        return ERROR_SUCCESS;
    }

    do 
    {
        DWORD write;
        sz = 1024;
        rc = MSI_RecordReadStream(row,2,buffer,&sz);
        if (rc != ERROR_SUCCESS)
        {
            ERR("Failed to get stream\n");
            DeleteFileW(FilePath);
            break;
        }
        WriteFile(the_file,buffer,sz,&write,NULL);
    } while (sz == 1024);

4216
    msi_free(FilePath);
4217
    CloseHandle(the_file);
4218

4219 4220
    return ERROR_SUCCESS;
}
4221

4222 4223 4224
static UINT msi_publish_icons(MSIPACKAGE *package)
{
    static const WCHAR query[]= {
4225 4226 4227 4228
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','I','c','o','n','`',0};
    MSIQUERY *view;
    UINT r;
4229 4230 4231 4232

    r = MSI_DatabaseOpenViewW(package->db, query, &view);
    if (r == ERROR_SUCCESS)
    {
4233
        r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4234
        msiobj_release(&view->hdr);
4235 4236
        if (r != ERROR_SUCCESS)
            return r;
4237 4238 4239 4240
    }
    return ERROR_SUCCESS;
}

4241
static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4242 4243
{
    UINT r;
4244
    HKEY source;
4245 4246 4247 4248
    LPWSTR buffer;
    MSIMEDIADISK *disk;
    MSISOURCELISTINFO *info;

4249 4250 4251 4252 4253
    r = RegCreateKeyW(hkey, szSourceList, &source);
    if (r != ERROR_SUCCESS)
        return r;

    RegCloseKey(source);
4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275

    buffer = strrchrW(package->PackagePath, '\\') + 1;
    r = MsiSourceListSetInfoW(package->ProductCode, NULL,
                              package->Context, MSICODE_PRODUCT,
                              INSTALLPROPERTY_PACKAGENAMEW, buffer);
    if (r != ERROR_SUCCESS)
        return r;

    r = MsiSourceListSetInfoW(package->ProductCode, NULL,
                              package->Context, MSICODE_PRODUCT,
                              INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
    if (r != ERROR_SUCCESS)
        return r;

    r = MsiSourceListSetInfoW(package->ProductCode, NULL,
                              package->Context, MSICODE_PRODUCT,
                              INSTALLPROPERTY_DISKPROMPTW, szEmpty);
    if (r != ERROR_SUCCESS)
        return r;

    LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
    {
4276
        if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294
            msi_set_last_used_source(package->ProductCode, NULL, info->context,
                                     info->options, info->value);
        else
            MsiSourceListSetInfoW(package->ProductCode, NULL,
                                  info->context, info->options,
                                  info->property, info->value);
    }

    LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
    {
        MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
                                   disk->context, disk->options,
                                   disk->disk_id, disk->volume_label, disk->disk_prompt);
    }

    return ERROR_SUCCESS;
}

4295 4296 4297 4298
static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
{
    static const WCHAR szARPProductIcon[] =
        {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4299 4300 4301 4302 4303 4304 4305
    static const WCHAR szAssignment[] =
        {'A','s','s','i','g','n','m','e','n','t',0};
    static const WCHAR szAdvertiseFlags[] =
        {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
    static const WCHAR szClients[] =
        {'C','l','i','e','n','t','s',0};
    static const WCHAR szColon[] = {':',0};
4306 4307
    WCHAR *buffer, *ptr, *guids, packcode[SQUASHED_GUID_SIZE];
    DWORD langid;
4308

4309
    buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4310 4311 4312
    msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
    msi_free(buffer);

4313
    langid = msi_get_property_int(package->db, szProductLanguage, 0);
4314 4315 4316 4317 4318
    msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);

    /* FIXME */
    msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);

4319
    buffer = msi_dup_property(package->db, szARPProductIcon);
4320 4321
    if (buffer)
    {
4322
        LPWSTR path = msi_build_icon_path(package, buffer);
4323 4324 4325 4326 4327
        msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
        msi_free(path);
        msi_free(buffer);
    }

4328
    buffer = msi_dup_property(package->db, szProductVersion);
4329 4330 4331 4332 4333 4334 4335
    if (buffer)
    {
        DWORD verdword = msi_version_str_to_dword(buffer);
        msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
        msi_free(buffer);
    }

4336 4337 4338 4339 4340
    msi_reg_set_val_dword(hkey, szAssignment, 0);
    msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
    msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
    msi_reg_set_val_str(hkey, szClients, szColon);

4341 4342
    if (!(guids = msi_get_package_code(package->db))) return ERROR_OUTOFMEMORY;
    if ((ptr = strchrW(guids, ';'))) *ptr = 0;
4343
    squash_guid(guids, packcode);
4344
    msi_free( guids);
4345
    msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4346 4347 4348 4349

    return ERROR_SUCCESS;
}

4350 4351 4352 4353
static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
{
    UINT r;
    HKEY hkey;
4354
    WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4355

4356
    upgrade = msi_dup_property(package->db, szUpgradeCode);
4357 4358 4359
    if (!upgrade)
        return ERROR_SUCCESS;

4360 4361 4362 4363
    if (package->Context == MSIINSTALLCONTEXT_MACHINE)
        r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
    else
        r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4364

4365 4366 4367 4368 4369 4370
    if (r != ERROR_SUCCESS)
    {
        WARN("failed to open upgrade code key\n");
        msi_free(upgrade);
        return ERROR_SUCCESS;
    }
4371 4372 4373 4374
    squash_guid(package->ProductCode, squashed_pc);
    msi_reg_set_val_str(hkey, squashed_pc, NULL);
    RegCloseKey(hkey);
    msi_free(upgrade);
4375
    return ERROR_SUCCESS;
4376 4377
}

4378 4379 4380 4381 4382 4383
static BOOL msi_check_publish(MSIPACKAGE *package)
{
    MSIFEATURE *feature;

    LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
    {
4384
        feature->Action = msi_get_feature_action( package, feature );
4385
        if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4386 4387 4388 4389 4390 4391
            return TRUE;
    }

    return FALSE;
}

4392 4393 4394 4395 4396 4397
static BOOL msi_check_unpublish(MSIPACKAGE *package)
{
    MSIFEATURE *feature;

    LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
    {
4398 4399
        feature->Action = msi_get_feature_action( package, feature );
        if (feature->Action != INSTALLSTATE_ABSENT)
4400 4401 4402 4403 4404 4405
            return FALSE;
    }

    return TRUE;
}

4406
static UINT msi_publish_patches( MSIPACKAGE *package )
4407
{
4408
    static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4409
    WCHAR patch_squashed[GUID_SIZE];
4410
    HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4411
    LONG res;
4412
    MSIPATCHINFO *patch;
4413
    UINT r;
4414 4415
    WCHAR *p, *all_patches = NULL;
    DWORD len = 0;
4416

4417
    r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4418
    if (r != ERROR_SUCCESS)
4419 4420
        return ERROR_FUNCTION_FAILED;

4421 4422 4423 4424 4425 4426 4427
    res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
    if (res != ERROR_SUCCESS)
    {
        r = ERROR_FUNCTION_FAILED;
        goto done;
    }

4428 4429 4430 4431
    r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
    if (r != ERROR_SUCCESS)
        goto done;

4432 4433 4434 4435 4436
    LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
    {
        squash_guid( patch->patchcode, patch_squashed );
        len += strlenW( patch_squashed ) + 1;
    }
4437

4438 4439
    p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
    if (!all_patches)
4440 4441
        goto done;

4442 4443
    LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
    {
4444 4445
        HKEY patch_key;

4446 4447 4448
        squash_guid( patch->patchcode, p );
        p += strlenW( p ) + 1;

4449
        res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4450 4451 4452 4453
                              (const BYTE *)patch->transforms,
                              (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
        if (res != ERROR_SUCCESS)
            goto done;
4454 4455 4456 4457 4458

        r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
        if (r != ERROR_SUCCESS)
            goto done;

4459 4460
        res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
                              (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4461 4462 4463 4464
        RegCloseKey( patch_key );
        if (res != ERROR_SUCCESS)
            goto done;

4465 4466 4467 4468 4469 4470
        if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
        {
            res = GetLastError();
            ERR("Unable to copy patch package %d\n", res);
            goto done;
        }
4471
        res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4472 4473 4474
        if (res != ERROR_SUCCESS)
            goto done;

4475 4476 4477 4478 4479 4480 4481 4482 4483 4484
        res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
                              sizeof(patch->state) );
        if (res != ERROR_SUCCESS)
        {
            RegCloseKey( patch_key );
            goto done;
        }

        res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
                              sizeof(patch->uninstallable) );
4485 4486 4487
        RegCloseKey( patch_key );
        if (res != ERROR_SUCCESS)
            goto done;
4488 4489 4490
    }

    all_patches[len] = 0;
4491
    res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4492
                          (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4493 4494 4495 4496 4497 4498 4499
    if (res != ERROR_SUCCESS)
        goto done;

    res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
                          (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
    if (res != ERROR_SUCCESS)
        r = ERROR_FUNCTION_FAILED;
4500 4501

done:
4502 4503
    RegCloseKey( product_patches_key );
    RegCloseKey( patches_key );
4504
    RegCloseKey( product_key );
4505
    msi_free( all_patches );
4506 4507 4508
    return r;
}

4509
static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4510 4511
{
    UINT rc;
4512 4513
    HKEY hukey = NULL, hudkey = NULL;
    MSIRECORD *uirow;
4514
    BOOL republish = FALSE;
4515

4516 4517 4518
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szPublishProduct);

4519 4520 4521 4522 4523 4524 4525
    if (!list_empty(&package->patches))
    {
        rc = msi_publish_patches(package);
        if (rc != ERROR_SUCCESS)
            goto end;
    }

4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553
    rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
                               &hukey, FALSE);
    if (rc == ERROR_SUCCESS)
    {
        WCHAR *package_code;

        package_code = msi_reg_get_val_str(hukey, INSTALLPROPERTY_PACKAGECODEW);
        if (package_code)
        {
            WCHAR *guid;

            guid = msi_get_package_code(package->db);
            if (guid)
            {
                WCHAR packed[SQUASHED_GUID_SIZE];

                squash_guid(guid, packed);
                msi_free(guid);
                if (!strcmpW(packed, package_code))
                {
                    TRACE("re-publishing product - new package\n");
                    republish = TRUE;
                }
            }
            msi_free(package_code);
        }
    }

4554
    /* FIXME: also need to publish if the product is in advertise mode */
4555 4556 4557 4558
    if (!republish && !msi_check_publish(package))
    {
        if (hukey)
            RegCloseKey(hukey);
4559
        return ERROR_SUCCESS;
4560
    }
4561

4562 4563 4564 4565 4566 4567 4568
    if (!hukey)
    {
        rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
                                   &hukey, TRUE);
        if (rc != ERROR_SUCCESS)
            goto end;
    }
4569

4570 4571 4572 4573
    rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
                                       NULL, &hudkey, TRUE);
    if (rc != ERROR_SUCCESS)
        goto end;
4574

4575 4576 4577
    rc = msi_publish_upgrade_code(package);
    if (rc != ERROR_SUCCESS)
        goto end;
4578 4579 4580

    rc = msi_publish_product_properties(package, hukey);
    if (rc != ERROR_SUCCESS)
4581
        goto end;
Aric Stewart's avatar
Aric Stewart committed
4582

4583
    rc = msi_publish_sourcelist(package, hukey);
4584 4585
    if (rc != ERROR_SUCCESS)
        goto end;
4586

4587
    rc = msi_publish_icons(package);
4588

4589
end:
4590 4591
    uirow = MSI_CreateRecord( 1 );
    MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4592
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4593 4594
    msiobj_release( &uirow->hdr );

4595
    RegCloseKey(hukey);
4596
    RegCloseKey(hudkey);
4597 4598 4599
    return rc;
}

4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613
static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
{
    WCHAR *filename, *ptr, *folder, *ret;
    const WCHAR *dirprop;

    filename = msi_dup_record_field( row, 2 );
    if (filename && (ptr = strchrW( filename, '|' )))
        ptr++;
    else
        ptr = filename;

    dirprop = MSI_RecordGetString( row, 3 );
    if (dirprop)
    {
4614 4615
        folder = strdupW( msi_get_target_folder( package, dirprop ) );
        if (!folder) folder = msi_dup_property( package->db, dirprop );
4616 4617
    }
    else
4618
        folder = msi_dup_property( package->db, szWindowsFolder );
4619 4620 4621 4622 4623 4624 4625 4626

    if (!folder)
    {
        ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
        msi_free( filename );
        return NULL;
    }

4627
    ret = msi_build_directory_name( 2, folder, ptr );
4628 4629 4630 4631 4632 4633

    msi_free( filename );
    msi_free( folder );
    return ret;
}

4634
static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4635
{
4636
    MSIPACKAGE *package = param;
4637 4638
    LPCWSTR component, section, key, value, identifier;
    LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4639
    MSIRECORD * uirow;
4640 4641
    INT action;
    MSICOMPONENT *comp;
4642

4643
    component = MSI_RecordGetString(row, 8);
4644
    comp = msi_get_loaded_component(package,component);
4645 4646
    if (!comp)
        return ERROR_SUCCESS;
4647

4648 4649
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
4650
    {
4651
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4652 4653
        return ERROR_SUCCESS;
    }
4654

4655 4656 4657 4658 4659
    identifier = MSI_RecordGetString(row,1); 
    section = MSI_RecordGetString(row,4);
    key = MSI_RecordGetString(row,5);
    value = MSI_RecordGetString(row,6);
    action = MSI_RecordGetInteger(row,7);
4660

4661 4662 4663
    deformat_string(package,section,&deformated_section);
    deformat_string(package,key,&deformated_key);
    deformat_string(package,value,&deformated_value);
4664

4665
    fullname = get_ini_file_name(package, row);
4666

4667 4668 4669
    if (action == 0)
    {
        TRACE("Adding value %s to section %s in %s\n",
4670 4671
                debugstr_w(deformated_key), debugstr_w(deformated_section),
                debugstr_w(fullname));
4672 4673 4674 4675 4676 4677 4678 4679 4680
        WritePrivateProfileStringW(deformated_section, deformated_key,
                                   deformated_value, fullname);
    }
    else if (action == 1)
    {
        WCHAR returned[10];
        GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
                                 returned, 10, fullname);
        if (returned[0] == 0)
4681
        {
4682
            TRACE("Adding value %s to section %s in %s\n",
4683 4684 4685
                    debugstr_w(deformated_key), debugstr_w(deformated_section),
                    debugstr_w(fullname));

4686
            WritePrivateProfileStringW(deformated_section, deformated_key,
4687 4688
                                       deformated_value, fullname);
        }
4689 4690 4691 4692 4693 4694 4695 4696 4697
    }
    else if (action == 3)
        FIXME("Append to existing section not yet implemented\n");

    uirow = MSI_CreateRecord(4);
    MSI_RecordSetStringW(uirow,1,identifier);
    MSI_RecordSetStringW(uirow,2,deformated_section);
    MSI_RecordSetStringW(uirow,3,deformated_key);
    MSI_RecordSetStringW(uirow,4,deformated_value);
4698
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4699
    msiobj_release( &uirow->hdr );
4700

4701 4702 4703 4704
    msi_free(fullname);
    msi_free(deformated_key);
    msi_free(deformated_value);
    msi_free(deformated_section);
4705 4706 4707 4708 4709
    return ERROR_SUCCESS;
}

static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
{
4710 4711 4712 4713
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','I','n','i','F','i','l','e','`',0};
    MSIQUERY *view;
4714 4715
    UINT rc;

4716 4717 4718
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szWriteIniValues);

4719
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4720 4721 4722 4723
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4724 4725 4726 4727
    msiobj_release(&view->hdr);
    return rc;
}

4728 4729 4730 4731 4732 4733 4734 4735 4736 4737
static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPCWSTR component, section, key, value, identifier;
    LPWSTR deformated_section, deformated_key, deformated_value, filename;
    MSICOMPONENT *comp;
    MSIRECORD *uirow;
    INT action;

    component = MSI_RecordGetString( row, 8 );
4738
    comp = msi_get_loaded_component( package, component );
4739 4740 4741
    if (!comp)
        return ERROR_SUCCESS;

4742 4743
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
4744
    {
4745
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780
        return ERROR_SUCCESS;
    }

    identifier = MSI_RecordGetString( row, 1 );
    section = MSI_RecordGetString( row, 4 );
    key = MSI_RecordGetString( row, 5 );
    value = MSI_RecordGetString( row, 6 );
    action = MSI_RecordGetInteger( row, 7 );

    deformat_string( package, section, &deformated_section );
    deformat_string( package, key, &deformated_key );
    deformat_string( package, value, &deformated_value );

    if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
    {
        filename = get_ini_file_name( package, row );

        TRACE("Removing key %s from section %s in %s\n",
               debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));

        if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
        {
            WARN("Unable to remove key %u\n", GetLastError());
        }
        msi_free( filename );
    }
    else
        FIXME("Unsupported action %d\n", action);


    uirow = MSI_CreateRecord( 4 );
    MSI_RecordSetStringW( uirow, 1, identifier );
    MSI_RecordSetStringW( uirow, 2, deformated_section );
    MSI_RecordSetStringW( uirow, 3, deformated_key );
    MSI_RecordSetStringW( uirow, 4, deformated_value );
4781
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799
    msiobj_release( &uirow->hdr );

    msi_free( deformated_key );
    msi_free( deformated_value );
    msi_free( deformated_section );
    return ERROR_SUCCESS;
}

static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPCWSTR component, section, key, value, identifier;
    LPWSTR deformated_section, deformated_key, deformated_value, filename;
    MSICOMPONENT *comp;
    MSIRECORD *uirow;
    INT action;

    component = MSI_RecordGetString( row, 8 );
4800
    comp = msi_get_loaded_component( package, component );
4801 4802 4803
    if (!comp)
        return ERROR_SUCCESS;

4804 4805
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
4806
    {
4807
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841
        return ERROR_SUCCESS;
    }

    identifier = MSI_RecordGetString( row, 1 );
    section = MSI_RecordGetString( row, 4 );
    key = MSI_RecordGetString( row, 5 );
    value = MSI_RecordGetString( row, 6 );
    action = MSI_RecordGetInteger( row, 7 );

    deformat_string( package, section, &deformated_section );
    deformat_string( package, key, &deformated_key );
    deformat_string( package, value, &deformated_value );

    if (action == msidbIniFileActionRemoveLine)
    {
        filename = get_ini_file_name( package, row );

        TRACE("Removing key %s from section %s in %s\n",
               debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));

        if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
        {
            WARN("Unable to remove key %u\n", GetLastError());
        }
        msi_free( filename );
    }
    else
        FIXME("Unsupported action %d\n", action);

    uirow = MSI_CreateRecord( 4 );
    MSI_RecordSetStringW( uirow, 1, identifier );
    MSI_RecordSetStringW( uirow, 2, deformated_section );
    MSI_RecordSetStringW( uirow, 3, deformated_key );
    MSI_RecordSetStringW( uirow, 4, deformated_value );
4842
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4843 4844 4845 4846 4847 4848 4849 4850 4851 4852
    msiobj_release( &uirow->hdr );

    msi_free( deformated_key );
    msi_free( deformated_value );
    msi_free( deformated_section );
    return ERROR_SUCCESS;
}

static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
{
4853 4854 4855 4856 4857 4858
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','I','n','i','F','i','l','e','`',0};
    static const WCHAR remove_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4859
    MSIQUERY *view;
4860
    UINT rc;
4861

4862 4863 4864
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveIniValues);

4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
        msiobj_release( &view->hdr );
        if (rc != ERROR_SUCCESS)
            return rc;
    }
    rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
        msiobj_release( &view->hdr );
        if (rc != ERROR_SUCCESS)
            return rc;
    }
    return ERROR_SUCCESS;
}

4884 4885
static void register_dll( const WCHAR *dll, BOOL unregister )
{
4886 4887 4888 4889 4890 4891 4892
    static const WCHAR regW[] =
        {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
    static const WCHAR unregW[] =
        {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
    PROCESS_INFORMATION pi;
    STARTUPINFOW si;
    WCHAR *cmd;
4893

4894
    if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4895

4896 4897 4898 4899 4900 4901 4902 4903 4904
    if (unregister) sprintfW( cmd, unregW, dll );
    else sprintfW( cmd, regW, dll );

    memset( &si, 0, sizeof(STARTUPINFOW) );
    if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
    {
        CloseHandle( pi.hThread );
        msi_dialog_check_messages( pi.hProcess );
        CloseHandle( pi.hProcess );
4905
    }
4906
    msi_free( cmd );
4907 4908
}

4909
static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4910
{
4911
    MSIPACKAGE *package = param;
4912
    LPCWSTR filename;
4913
    MSIFILE *file;
4914
    MSIRECORD *uirow;
Aric Stewart's avatar
Aric Stewart committed
4915

4916
    filename = MSI_RecordGetString( row, 1 );
4917
    file = msi_get_loaded_file( package, filename );
4918
    if (!file)
4919
    {
4920 4921 4922 4923 4924 4925 4926
        WARN("unable to find file %s\n", debugstr_w(filename));
        return ERROR_SUCCESS;
    }
    file->Component->Action = msi_get_component_action( package, file->Component );
    if (file->Component->Action != INSTALLSTATE_LOCAL)
    {
        TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4927
        return ERROR_SUCCESS;
4928 4929
    }

4930 4931
    TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
    register_dll( file->TargetPath, FALSE );
4932

4933
    uirow = MSI_CreateRecord( 2 );
4934
    MSI_RecordSetStringW( uirow, 1, file->File );
4935
    MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4936
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4937 4938
    msiobj_release( &uirow->hdr );

4939 4940
    return ERROR_SUCCESS;
}
4941

4942 4943
static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
{
4944 4945 4946 4947
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','S','e','l','f','R','e','g','`',0};
    MSIQUERY *view;
4948
    UINT rc;
4949

4950 4951 4952
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szSelfRegModules);

4953
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4954 4955 4956
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

4957
    rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4958
    msiobj_release(&view->hdr);
4959
    return rc;
4960 4961
}

4962 4963 4964 4965 4966 4967 4968 4969
static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPCWSTR filename;
    MSIFILE *file;
    MSIRECORD *uirow;

    filename = MSI_RecordGetString( row, 1 );
4970
    file = msi_get_loaded_file( package, filename );
4971 4972
    if (!file)
    {
4973 4974 4975 4976 4977 4978 4979
        WARN("unable to find file %s\n", debugstr_w(filename));
        return ERROR_SUCCESS;
    }
    file->Component->Action = msi_get_component_action( package, file->Component );
    if (file->Component->Action != INSTALLSTATE_ABSENT)
    {
        TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4980 4981 4982
        return ERROR_SUCCESS;
    }

4983 4984
    TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
    register_dll( file->TargetPath, TRUE );
4985 4986

    uirow = MSI_CreateRecord( 2 );
4987
    MSI_RecordSetStringW( uirow, 1, file->File );
4988
    MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4989
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4990 4991 4992 4993 4994 4995 4996
    msiobj_release( &uirow->hdr );

    return ERROR_SUCCESS;
}

static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
{
4997 4998 4999
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','S','e','l','f','R','e','g','`',0};
5000
    MSIQUERY *view;
5001
    UINT rc;
5002

5003 5004 5005
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szSelfUnregModules);

5006 5007 5008 5009
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

5010
    rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
5011
    msiobj_release( &view->hdr );
5012
    return rc;
5013 5014
}

5015 5016
static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
{
5017
    MSIFEATURE *feature;
5018
    UINT rc;
5019
    HKEY hkey = NULL, userdata = NULL;
5020

5021 5022 5023
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szPublishFeatures);

5024 5025 5026
    if (!msi_check_publish(package))
        return ERROR_SUCCESS;

5027
    rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5028 5029 5030 5031
                                &hkey, TRUE);
    if (rc != ERROR_SUCCESS)
        goto end;

5032
    rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5033 5034 5035
                                        &userdata, TRUE);
    if (rc != ERROR_SUCCESS)
        goto end;
5036

5037
    /* here the guids are base 85 encoded */
5038
    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5039
    {
5040
        ComponentList *cl;
5041 5042 5043
        LPWSTR data = NULL;
        GUID clsid;
        INT size;
5044
        BOOL absent = FALSE;
5045
        MSIRECORD *uirow;
5046

5047
        if (feature->Level <= 0) continue;
5048 5049
        if (feature->Action == INSTALLSTATE_UNKNOWN &&
                feature->Installed != INSTALLSTATE_ABSENT) continue;
5050

5051 5052 5053
        if (feature->Action != INSTALLSTATE_LOCAL &&
            feature->Action != INSTALLSTATE_SOURCE &&
            feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
5054

5055
        size = 1;
5056
        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5057 5058 5059
        {
            size += 21;
        }
5060
        if (feature->Feature_Parent)
5061
            size += strlenW( feature->Feature_Parent )+2;
5062

5063
        data = msi_alloc(size * sizeof(WCHAR));
5064 5065

        data[0] = 0;
5066
        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5067
        {
5068
            MSICOMPONENT* component = cl->component;
5069
            WCHAR buf[21];
5070

5071
            buf[0] = 0;
5072
            if (component->ComponentId)
5073
            {
5074 5075
                TRACE("From %s\n",debugstr_w(component->ComponentId));
                CLSIDFromString(component->ComponentId, &clsid);
5076 5077 5078 5079
                encode_base85_guid(&clsid,buf);
                TRACE("to %s\n",debugstr_w(buf));
                strcatW(data,buf);
            }
5080
        }
5081

5082
        if (feature->Feature_Parent)
5083 5084 5085
        {
            static const WCHAR sep[] = {'\2',0};
            strcatW(data,sep);
5086
            strcatW(data,feature->Feature_Parent);
5087 5088
        }

5089
        msi_reg_set_val_str( userdata, feature->Feature, data );
5090
        msi_free(data);
5091

5092 5093 5094
        size = 0;
        if (feature->Feature_Parent)
            size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
5095 5096
        if (!absent)
        {
5097
            size += sizeof(WCHAR);
5098
            RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5099
                           (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
5100 5101 5102
        }
        else
        {
5103
            size += 2*sizeof(WCHAR);
5104
            data = msi_alloc(size);
5105
            data[0] = 0x6;
5106 5107 5108
            data[1] = 0;
            if (feature->Feature_Parent)
                strcpyW( &data[1], feature->Feature_Parent );
5109
            RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5110
                       (LPBYTE)data,size);
5111
            msi_free(data);
5112
        }
5113 5114 5115 5116

        /* the UI chunk */
        uirow = MSI_CreateRecord( 1 );
        MSI_RecordSetStringW( uirow, 1, feature->Feature );
5117
        MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5118
        msiobj_release( &uirow->hdr );
5119
        /* FIXME: call msi_ui_progress? */
5120 5121 5122
    }

end:
5123 5124
    RegCloseKey(hkey);
    RegCloseKey(userdata);
5125 5126 5127
    return rc;
}

5128 5129 5130 5131
static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
{
    UINT r;
    HKEY hkey;
5132
    MSIRECORD *uirow;
5133 5134 5135

    TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));

5136
    r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5137
                               &hkey, FALSE);
5138 5139 5140 5141 5142 5143
    if (r == ERROR_SUCCESS)
    {
        RegDeleteValueW(hkey, feature->Feature);
        RegCloseKey(hkey);
    }

5144
    r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5145
                                       &hkey, FALSE);
5146 5147 5148 5149 5150 5151
    if (r == ERROR_SUCCESS)
    {
        RegDeleteValueW(hkey, feature->Feature);
        RegCloseKey(hkey);
    }

5152 5153
    uirow = MSI_CreateRecord( 1 );
    MSI_RecordSetStringW( uirow, 1, feature->Feature );
5154
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5155 5156
    msiobj_release( &uirow->hdr );

5157 5158 5159 5160 5161 5162 5163
    return ERROR_SUCCESS;
}

static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
{
    MSIFEATURE *feature;

5164 5165 5166
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szUnpublishFeatures);

5167
    if (!msi_check_unpublish(package))
5168 5169 5170 5171 5172 5173 5174 5175 5176 5177
        return ERROR_SUCCESS;

    LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
    {
        msi_unpublish_feature(package, feature);
    }

    return ERROR_SUCCESS;
}

5178
static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5179
{
5180 5181
    static const WCHAR date_fmt[] =
        {'%','i','%','0','2','i','%','0','2','i',0};
5182 5183 5184 5185
    static const WCHAR szEstimatedSize[] =
        {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
    static const WCHAR szDisplayVersion[] =
        {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233
    static const WCHAR szInstallSource[] =
        {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
    static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
        {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
    static const WCHAR szAuthorizedCDFPrefix[] =
        {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
    static const WCHAR szARPCONTACT[] =
        {'A','R','P','C','O','N','T','A','C','T',0};
    static const WCHAR szContact[] =
        {'C','o','n','t','a','c','t',0};
    static const WCHAR szARPCOMMENTS[] =
        {'A','R','P','C','O','M','M','E','N','T','S',0};
    static const WCHAR szComments[] =
        {'C','o','m','m','e','n','t','s',0};
    static const WCHAR szProductName[] =
        {'P','r','o','d','u','c','t','N','a','m','e',0};
    static const WCHAR szDisplayName[] =
        {'D','i','s','p','l','a','y','N','a','m','e',0};
    static const WCHAR szARPHELPLINK[] =
        {'A','R','P','H','E','L','P','L','I','N','K',0};
    static const WCHAR szHelpLink[] =
        {'H','e','l','p','L','i','n','k',0};
    static const WCHAR szARPHELPTELEPHONE[] =
        {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
    static const WCHAR szHelpTelephone[] =
        {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
    static const WCHAR szARPINSTALLLOCATION[] =
        {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
    static const WCHAR szManufacturer[] =
        {'M','a','n','u','f','a','c','t','u','r','e','r',0};
    static const WCHAR szPublisher[] =
        {'P','u','b','l','i','s','h','e','r',0};
    static const WCHAR szARPREADME[] =
        {'A','R','P','R','E','A','D','M','E',0};
    static const WCHAR szReadme[] =
        {'R','e','a','d','M','e',0};
    static const WCHAR szARPSIZE[] =
        {'A','R','P','S','I','Z','E',0};
    static const WCHAR szSize[] =
        {'S','i','z','e',0};
    static const WCHAR szARPURLINFOABOUT[] =
        {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
    static const WCHAR szURLInfoAbout[] =
        {'U','R','L','I','n','f','o','A','b','o','u','t',0};
    static const WCHAR szARPURLUPDATEINFO[] =
        {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
    static const WCHAR szURLUpdateInfo[] =
        {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5234 5235 5236 5237
    static const WCHAR szARPSYSTEMCOMPONENT[] =
        {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
    static const WCHAR szSystemComponent[] =
        {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249
    static const WCHAR szARPNOMODIFY[] =
        {'A','R','P','N','O','M','O','D','I','F','Y',0};
    static const WCHAR szNoModify[] =
        {'N','o','M','o','d','i','f','y',0};
    static const WCHAR szARPNOREMOVE[] =
        {'A','R','P','N','O','R','E','M','O','V','E',0};
    static const WCHAR szNoRemove[] =
        {'N','o','R','e','m','o','v','e',0};
    static const WCHAR szARPNOREPAIR[] =
        {'A','R','P','N','O','R','E','P','A','I','R',0};
    static const WCHAR szNoRepair[] =
        {'N','o','R','e','p','a','i','r',0};
5250 5251 5252 5253 5254 5255 5256 5257 5258

    static const WCHAR *propval[] = {
        szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
        szARPCONTACT,             szContact,
        szARPCOMMENTS,            szComments,
        szProductName,            szDisplayName,
        szARPHELPLINK,            szHelpLink,
        szARPHELPTELEPHONE,       szHelpTelephone,
        szARPINSTALLLOCATION,     szInstallLocation,
5259
        szSourceDir,              szInstallSource,
5260 5261 5262 5263 5264 5265
        szManufacturer,           szPublisher,
        szARPREADME,              szReadme,
        szARPSIZE,                szSize,
        szARPURLINFOABOUT,        szURLInfoAbout,
        szARPURLUPDATEINFO,       szURLUpdateInfo,
        NULL
5266
    };
5267
    const WCHAR **p = propval;
5268 5269 5270 5271
    SYSTEMTIME systime;
    DWORD size, langid;
    WCHAR date[9], *val, *buffer;
    const WCHAR *prop, *key;
5272

5273
    while (*p)
5274
    {
5275 5276
        prop = *p++;
        key = *p++;
5277
        val = msi_dup_property(package->db, prop);
5278
        msi_reg_set_val_str(hkey, key, val);
5279 5280
        msi_free(val);
    }
5281 5282

    msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5283 5284 5285 5286
    if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
    {
        msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
    }
5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317

    if (msi_get_property_int( package->db, szARPNOREMOVE, 0 ))
        msi_reg_set_val_dword( hkey, szNoRemove, 1 );
    else
    {
        static const WCHAR fmt_install[] =
            {'M','s','i','E','x','e','c','.','e','x','e',' ',
             '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
        static const WCHAR fmt_uninstall[] =
            {'M','s','i','E','x','e','c','.','e','x','e',' ',
             '/','X','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
        static const WCHAR szModifyPath[] =
            {'M','o','d','i','f','y','P','a','t','h',0};
        static const WCHAR szUninstallString[] =
            {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
        const WCHAR *fmt = fmt_install;

        if (msi_get_property_int( package->db, szARPNOREPAIR, 0 ))
            msi_reg_set_val_dword( hkey, szNoRepair, 1 );

        if (msi_get_property_int( package->db, szARPNOMODIFY, 0 ))
        {
            msi_reg_set_val_dword( hkey, szNoModify, 1 );
            fmt = fmt_uninstall;
        }

        size = deformat_string(package, fmt, &buffer) * sizeof(WCHAR);
        RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
        RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
        msi_free(buffer);
    }
5318 5319 5320 5321 5322 5323 5324 5325

    /* FIXME: Write real Estimated Size when we have it */
    msi_reg_set_val_dword(hkey, szEstimatedSize, 0);

    GetLocalTime(&systime);
    sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
    msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);

5326
    langid = msi_get_property_int(package->db, szProductLanguage, 0);
5327 5328
    msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);

5329
    buffer = msi_dup_property(package->db, szProductVersion);
5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340
    msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
    if (buffer)
    {
        DWORD verdword = msi_version_str_to_dword(buffer);

        msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
        msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
        msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
        msi_free(buffer);
    }

5341 5342 5343
    return ERROR_SUCCESS;
}

Aric Stewart's avatar
Aric Stewart committed
5344 5345
static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
{
5346
    WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5347
    MSIRECORD *uirow;
5348
    HKEY hkey, props, upgrade_key;
5349
    UINT rc;
5350

5351 5352 5353
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterProduct);

5354
    /* FIXME: also need to publish if the product is in advertise mode */
5355 5356
    if (!msi_get_property_int( package->db, szProductToBeRegistered, 0 )
            && !msi_check_publish(package))
5357
        return ERROR_SUCCESS;
Aric Stewart's avatar
Aric Stewart committed
5358

5359
    rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
Aric Stewart's avatar
Aric Stewart committed
5360
    if (rc != ERROR_SUCCESS)
5361
        return rc;
Aric Stewart's avatar
Aric Stewart committed
5362

5363
    rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5364 5365
    if (rc != ERROR_SUCCESS)
        goto done;
5366

5367 5368 5369
    rc = msi_publish_install_properties(package, hkey);
    if (rc != ERROR_SUCCESS)
        goto done;
5370

5371 5372 5373
    rc = msi_publish_install_properties(package, props);
    if (rc != ERROR_SUCCESS)
        goto done;
5374

5375
    upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5376 5377
    if (upgrade_code)
    {
5378 5379 5380 5381 5382 5383 5384 5385
        rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
        if (rc == ERROR_SUCCESS)
        {
            squash_guid( package->ProductCode, squashed_pc );
            msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
            RegCloseKey( upgrade_key );
        }
        msi_free( upgrade_code );
5386
    }
5387 5388
    msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
    package->delete_on_close = FALSE;
5389

5390
done:
5391 5392
    uirow = MSI_CreateRecord( 1 );
    MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5393
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5394
    msiobj_release( &uirow->hdr );
Aric Stewart's avatar
Aric Stewart committed
5395

5396
    RegCloseKey(hkey);
Aric Stewart's avatar
Aric Stewart committed
5397 5398 5399 5400 5401
    return ERROR_SUCCESS;
}

static UINT ACTION_InstallExecute(MSIPACKAGE *package)
{
5402
    return execute_script(package, SCRIPT_INSTALL);
Aric Stewart's avatar
Aric Stewart committed
5403 5404
}

5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443
static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    const WCHAR *icon = MSI_RecordGetString( row, 1 );
    WCHAR *p, *icon_path;

    if (!icon) return ERROR_SUCCESS;
    if ((icon_path = msi_build_icon_path( package, icon )))
    {
        TRACE("removing icon file %s\n", debugstr_w(icon_path));
        DeleteFileW( icon_path );
        if ((p = strrchrW( icon_path, '\\' )))
        {
            *p = 0;
            RemoveDirectoryW( icon_path );
        }
        msi_free( icon_path );
    }
    return ERROR_SUCCESS;
}

static UINT msi_unpublish_icons( MSIPACKAGE *package )
{
    static const WCHAR query[]= {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
    MSIQUERY *view;
    UINT r;

    r = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (r == ERROR_SUCCESS)
    {
        r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
        msiobj_release( &view->hdr );
        if (r != ERROR_SUCCESS)
            return r;
    }
    return ERROR_SUCCESS;
}

5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481
static void remove_product_upgrade_code( MSIPACKAGE *package )
{
    WCHAR *code, product[SQUASHED_GUID_SIZE];
    HKEY hkey;
    LONG res;
    DWORD count;

    squash_guid( package->ProductCode, product );
    if (!(code = msi_dup_property( package->db, szUpgradeCode )))
    {
        WARN( "upgrade code not found\n" );
        return;
    }
    if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
    {
        RegDeleteValueW( hkey, product );
        res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
        RegCloseKey( hkey );
        if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
    }
    if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
    {
        RegDeleteValueW( hkey, product );
        res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
        RegCloseKey( hkey );
        if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
    }
    if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
    {
        RegDeleteValueW( hkey, product );
        res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
        RegCloseKey( hkey );
        if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
    }

    msi_free( code );
}

5482
static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5483
{
5484
    MSIPATCHINFO *patch;
5485 5486

    MSIREG_DeleteProductKey(package->ProductCode);
5487
    MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5488
    MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5489

5490 5491 5492 5493
    MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
    MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
    MSIREG_DeleteUserProductKey(package->ProductCode);
    MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5494

5495
    remove_product_upgrade_code( package );
5496

5497 5498 5499
    LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
    {
        MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5500 5501 5502 5503 5504
        if (!strcmpW( package->ProductCode, patch->products ))
        {
            TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
            patch->delete_on_close = TRUE;
        }
5505
        /* FIXME: remove local patch package if this is the last product */
5506
    }
5507
    TRACE("removing local package %s\n", debugstr_w(package->localfile));
5508
    package->delete_on_close = TRUE;
5509 5510

    msi_unpublish_icons( package );
5511 5512 5513
    return ERROR_SUCCESS;
}

5514 5515 5516 5517 5518 5519
static BOOL is_full_uninstall( MSIPACKAGE *package )
{
    MSIFEATURE *feature;

    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
    {
5520 5521 5522
        if (feature->Action != INSTALLSTATE_ABSENT &&
                (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
            return FALSE;
5523 5524
    }

5525
    return TRUE;
5526 5527
}

Aric Stewart's avatar
Aric Stewart committed
5528 5529
static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
{
5530
    UINT rc;
5531

5532
    /* first do the same as an InstallExecute */
5533
    rc = execute_script(package, SCRIPT_INSTALL);
5534 5535
    if (rc != ERROR_SUCCESS)
        return rc;
5536

5537 5538
    /* then handle commit actions */
    rc = execute_script(package, SCRIPT_COMMIT);
5539 5540 5541
    if (rc != ERROR_SUCCESS)
        return rc;

5542 5543 5544
    if (is_full_uninstall(package))
        rc = ACTION_UnpublishProduct(package);

5545
    return rc;
Aric Stewart's avatar
Aric Stewart committed
5546 5547
}

5548
UINT ACTION_ForceReboot(MSIPACKAGE *package)
Aric Stewart's avatar
Aric Stewart committed
5549 5550 5551 5552 5553 5554
{
    static const WCHAR RunOnce[] = {
    'S','o','f','t','w','a','r','e','\\',
    'M','i','c','r','o','s','o','f','t','\\',
    'W','i','n','d','o','w','s','\\',
    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5555
    'R','u','n','O','n','c','e',0};
Aric Stewart's avatar
Aric Stewart committed
5556 5557 5558 5559 5560 5561
    static const WCHAR InstallRunOnce[] = {
    'S','o','f','t','w','a','r','e','\\',
    'M','i','c','r','o','s','o','f','t','\\',
    'W','i','n','d','o','w','s','\\',
    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
    'I','n','s','t','a','l','l','e','r','\\',
5562
    'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
Aric Stewart's avatar
Aric Stewart committed
5563 5564

    static const WCHAR msiexec_fmt[] = {
5565
    '%','s',
Aric Stewart's avatar
Aric Stewart committed
5566 5567 5568 5569 5570 5571
    '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
    '\"','%','s','\"',0};
    static const WCHAR install_fmt[] = {
    '/','I',' ','\"','%','s','\"',' ',
    'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
    'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5572
    WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5573
    HKEY hkey;
Aric Stewart's avatar
Aric Stewart committed
5574

5575
    squash_guid( package->ProductCode, squashed_pc );
Aric Stewart's avatar
Aric Stewart committed
5576

5577
    GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir));
Aric Stewart's avatar
Aric Stewart committed
5578
    RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5579
    snprintfW(buffer, ARRAY_SIZE(buffer), msiexec_fmt, sysdir, squashed_pc);
Aric Stewart's avatar
Aric Stewart committed
5580

5581
    msi_reg_set_val_str( hkey, squashed_pc, buffer );
Aric Stewart's avatar
Aric Stewart committed
5582 5583 5584 5585 5586
    RegCloseKey(hkey);

    TRACE("Reboot command %s\n",debugstr_w(buffer));

    RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5587
    sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5588

5589
    msi_reg_set_val_str( hkey, squashed_pc, buffer );
Aric Stewart's avatar
Aric Stewart committed
5590 5591
    RegCloseKey(hkey);

5592
    return ERROR_INSTALL_SUSPEND;
Aric Stewart's avatar
Aric Stewart committed
5593 5594
}

5595
static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5596
{
5597
    DWORD attrib;
5598
    UINT rc;
Mike McCormack's avatar
Mike McCormack committed
5599

5600
    /*
Mike McCormack's avatar
Mike McCormack committed
5601 5602
     * We are currently doing what should be done here in the top level Install
     * however for Administrative and uninstalls this step will be needed
5603
     */
5604 5605 5606
    if (!package->PackagePath)
        return ERROR_SUCCESS;

5607
    msi_set_sourcedir_props(package, TRUE);
5608

5609
    attrib = GetFileAttributesW(package->db->path);
5610 5611
    if (attrib == INVALID_FILE_ATTRIBUTES)
    {
5612 5613
        MSIRECORD *record;
        LPWSTR prompt;
5614 5615 5616
        DWORD size = 0;

        rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
5617
                package->Context, MSICODE_PRODUCT,
5618 5619 5620
                INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
        if (rc == ERROR_MORE_DATA)
        {
5621
            prompt = msi_alloc(size * sizeof(WCHAR));
5622
            MsiSourceListGetInfoW(package->ProductCode, NULL, 
5623
                    package->Context, MSICODE_PRODUCT,
5624 5625 5626
                    INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
        }
        else
5627
            prompt = strdupW(package->db->path);
5628

5629 5630 5631
        record = MSI_CreateRecord(2);
        MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
        MSI_RecordSetStringW(record, 2, prompt);
5632
        msi_free(prompt);
5633 5634
        while(attrib == INVALID_FILE_ATTRIBUTES)
        {
5635 5636
            MSI_RecordSetStringW(record, 0, NULL);
            rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5637
            if (rc == IDCANCEL)
5638
                return ERROR_INSTALL_USEREXIT;
5639
            attrib = GetFileAttributesW(package->db->path);
5640 5641 5642 5643 5644 5645 5646
        }
        rc = ERROR_SUCCESS;
    }
    else
        return ERROR_SUCCESS;

    return rc;
5647 5648
}

5649 5650
static UINT ACTION_RegisterUser(MSIPACKAGE *package)
{
5651 5652 5653 5654
    HKEY hkey = 0;
    LPWSTR buffer, productid = NULL;
    UINT i, rc = ERROR_SUCCESS;
    MSIRECORD *uirow;
5655 5656 5657

    static const WCHAR szPropKeys[][80] = 
    {
5658 5659 5660 5661
        {'P','r','o','d','u','c','t','I','D',0},
        {'U','S','E','R','N','A','M','E',0},
        {'C','O','M','P','A','N','Y','N','A','M','E',0},
        {0},
5662 5663 5664 5665
    };

    static const WCHAR szRegKeys[][80] = 
    {
5666 5667 5668 5669
        {'P','r','o','d','u','c','t','I','D',0},
        {'R','e','g','O','w','n','e','r',0},
        {'R','e','g','C','o','m','p','a','n','y',0},
        {0},
5670 5671
    };

5672 5673 5674
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterUser);

5675 5676
    if (msi_check_unpublish(package))
    {
5677
        MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5678
        goto end;
5679 5680
    }

5681
    productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5682
    if (!productid)
5683
        goto end;
5684

5685 5686
    rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
                                 NULL, &hkey, TRUE);
5687 5688 5689
    if (rc != ERROR_SUCCESS)
        goto end;

5690
    for( i = 0; szPropKeys[i][0]; i++ )
5691
    {
5692
        buffer = msi_dup_property( package->db, szPropKeys[i] );
5693
        msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5694
        msi_free( buffer );
5695 5696 5697
    }

end:
5698 5699
    uirow = MSI_CreateRecord( 1 );
    MSI_RecordSetStringW( uirow, 1, productid );
5700
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5701 5702
    msiobj_release( &uirow->hdr );

5703
    msi_free(productid);
5704
    RegCloseKey(hkey);
5705
    return rc;
5706 5707
}

5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722
static UINT iterate_properties(MSIRECORD *record, void *param)
{
    static const WCHAR prop_template[] =
        {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0};
    MSIRECORD *uirow;

    uirow = MSI_CloneRecord(record);
    if (!uirow) return ERROR_OUTOFMEMORY;
    MSI_RecordSetStringW(uirow, 0, prop_template);
    MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
    msiobj_release(&uirow->hdr);

    return ERROR_SUCCESS;
}

5723 5724 5725

static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
{
5726 5727 5728 5729
    static const WCHAR prop_query[] =
        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0};
    WCHAR *productname;
    WCHAR *action;
5730
    WCHAR *info_template;
5731
    MSIQUERY *view;
5732
    MSIRECORD *uirow, *uirow_info;
5733
    UINT rc;
5734

5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745
    /* Send COMMONDATA and INFO messages. */
    /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
    uirow = MSI_CreateRecord(3);
    if (!uirow) return ERROR_OUTOFMEMORY;
    MSI_RecordSetStringW(uirow, 0, NULL);
    MSI_RecordSetInteger(uirow, 1, 0);
    MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
    MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
    MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
    /* FIXME: send INSTALLMESSAGE_PROGRESS */
    MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761

    if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
    {
        uirow_info = MSI_CreateRecord(0);
        if (!uirow_info)
        {
            msiobj_release(&uirow->hdr);
            return ERROR_OUTOFMEMORY;
        }
        info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
        MSI_RecordSetStringW(uirow_info, 0, info_template);
        msi_free(info_template);
        MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
        msiobj_release(&uirow_info->hdr);
    }

5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794
    MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);

    productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
    MSI_RecordSetInteger(uirow, 1, 1);
    MSI_RecordSetStringW(uirow, 2, productname);
    MSI_RecordSetStringW(uirow, 3, NULL);
    MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
    msiobj_release(&uirow->hdr);

    package->LastActionResult = MSI_NULL_INTEGER;

    action = msi_dup_property(package->db, szEXECUTEACTION);
    if (!action) action = msi_strdupW(szINSTALL, strlenW(szINSTALL));

    /* Perform the action. Top-level actions trigger a sequence. */
    if (!strcmpW(action, szINSTALL))
    {
        /* Send ACTIONSTART/INFO and INSTALLSTART. */
        ui_actionstart(package, szINSTALL, NULL, NULL);
        ui_actioninfo(package, szINSTALL, TRUE, 0);
        uirow = MSI_CreateRecord(2);
        if (!uirow)
        {
            rc = ERROR_OUTOFMEMORY;
            goto end;
        }
        MSI_RecordSetStringW(uirow, 0, NULL);
        MSI_RecordSetStringW(uirow, 1, productname);
        MSI_RecordSetStringW(uirow, 2, package->ProductCode);
        MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
        msiobj_release(&uirow->hdr);

        /* Perform the installation. Always use the ExecuteSequence. */
5795
        package->InWhatSequence |= SEQUENCE_EXEC;
5796
        rc = ACTION_ProcessExecSequence(package);
5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813

        /* Send return value and INSTALLEND. */
        ui_actioninfo(package, szINSTALL, FALSE, !rc);
        uirow = MSI_CreateRecord(3);
        if (!uirow)
        {
            rc = ERROR_OUTOFMEMORY;
            goto end;
        }
        MSI_RecordSetStringW(uirow, 0, NULL);
        MSI_RecordSetStringW(uirow, 1, productname);
        MSI_RecordSetStringW(uirow, 2, package->ProductCode);
        MSI_RecordSetInteger(uirow, 3, !rc);
        MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
        msiobj_release(&uirow->hdr);
    }
    else
5814
        rc = ACTION_PerformAction(package, action);
5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840

    /* Send all set properties. */
    if (!MSI_OpenQuery(package->db, &view, prop_query))
    {
        MSI_IterateRecords(view, NULL, iterate_properties, package);
        msiobj_release(&view->hdr);
    }

    /* And finally, toggle the cancel off and on. */
    uirow = MSI_CreateRecord(2);
    if (!uirow)
    {
        rc = ERROR_OUTOFMEMORY;
        goto end;
    }
    MSI_RecordSetStringW(uirow, 0, NULL);
    MSI_RecordSetInteger(uirow, 1, 2);
    MSI_RecordSetInteger(uirow, 2, 0);
    MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
    MSI_RecordSetInteger(uirow, 2, 1);
    MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
    msiobj_release(&uirow->hdr);

end:
    msi_free(productname);
    msi_free(action);
5841 5842 5843
    return rc;
}

5844 5845 5846 5847 5848
static UINT ACTION_INSTALL(MSIPACKAGE *package)
{
    msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1);
    if (needs_ui_sequence(package) && ui_sequence_exists(package))
    {
5849
        package->InWhatSequence |= SEQUENCE_UI;
5850 5851 5852 5853 5854 5855
        return ACTION_ProcessUISequence(package);
    }
    else
        return ACTION_ExecuteAction(package);
}

5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883
WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
{
    static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
    WCHAR productid_85[21], component_85[21], *ret;
    GUID clsid;
    DWORD sz;

    /* > is used if there is a component GUID and < if not.  */

    productid_85[0] = 0;
    component_85[0] = 0;
    CLSIDFromString( package->ProductCode, &clsid );

    encode_base85_guid( &clsid, productid_85 );
    if (component)
    {
        CLSIDFromString( component->ComponentId, &clsid );
        encode_base85_guid( &clsid, component_85 );
    }

    TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
          debugstr_w(component_85));

    sz = 20 + strlenW( feature ) + 20 + 3;
    ret = msi_alloc_zero( sz * sizeof(WCHAR) );
    if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
    return ret;
}
5884

5885 5886
static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
{
5887
    MSIPACKAGE *package = param;
5888
    LPCWSTR compgroupid, component, feature, qualifier, text;
5889
    LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5890
    HKEY hkey = NULL;
5891
    UINT rc;
5892
    MSICOMPONENT *comp;
5893 5894
    MSIFEATURE *feat;
    DWORD sz;
5895
    MSIRECORD *uirow;
5896
    int len;
5897

5898
    feature = MSI_RecordGetString(rec, 5);
5899
    feat = msi_get_loaded_feature(package, feature);
5900
    if (!feat)
5901
        return ERROR_SUCCESS;
5902

5903 5904 5905 5906
    feat->Action = msi_get_feature_action( package, feat );
    if (feat->Action != INSTALLSTATE_LOCAL &&
        feat->Action != INSTALLSTATE_SOURCE &&
        feat->Action != INSTALLSTATE_ADVERTISED)
5907
    {
5908
        TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5909 5910
        return ERROR_SUCCESS;
    }
5911

5912
    component = MSI_RecordGetString(rec, 3);
5913
    comp = msi_get_loaded_component(package, component);
5914 5915 5916
    if (!comp)
        return ERROR_SUCCESS;

5917
    compgroupid = MSI_RecordGetString(rec,1);
5918
    qualifier = MSI_RecordGetString(rec,2);
5919 5920 5921 5922 5923

    rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
    if (rc != ERROR_SUCCESS)
        goto end;

5924
    advertise = msi_create_component_advertise_string( package, comp, feature );
5925
    text = MSI_RecordGetString( rec, 4 );
5926
    if (text)
5927 5928 5929 5930 5931 5932 5933 5934
    {
        p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
        strcpyW( p, advertise );
        strcatW( p, text );
        msi_free( advertise );
        advertise = p;
    }
    existing = msi_reg_get_val_str( hkey, qualifier );
5935

5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964
    sz = strlenW( advertise ) + 1;
    if (existing)
    {
        for (p = existing; *p; p += len)
        {
            len = strlenW( p ) + 1;
            if (strcmpW( advertise, p )) sz += len;
        }
    }
    if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
    {
        rc = ERROR_OUTOFMEMORY;
        goto end;
    }
    q = output;
    if (existing)
    {
        for (p = existing; *p; p += len)
        {
            len = strlenW( p ) + 1;
            if (strcmpW( advertise, p ))
            {
                memcpy( q, p, len * sizeof(WCHAR) );
                q += len;
            }
        }
    }
    strcpyW( q, advertise );
    q[strlenW( q ) + 1] = 0;
5965

5966
    msi_reg_set_val_multi_str( hkey, qualifier, output );
5967 5968 5969
    
end:
    RegCloseKey(hkey);
5970 5971 5972
    msi_free( output );
    msi_free( advertise );
    msi_free( existing );
5973 5974 5975 5976 5977

    /* the UI chunk */
    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, compgroupid );
    MSI_RecordSetStringW( uirow, 2, qualifier);
5978
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5979 5980 5981
    msiobj_release( &uirow->hdr );
    /* FIXME: call ui_progress? */

5982 5983 5984 5985 5986 5987 5988 5989 5990
    return rc;
}

/*
 * At present I am ignorning the advertised components part of this and only
 * focusing on the qualified component sets
 */
static UINT ACTION_PublishComponents(MSIPACKAGE *package)
{
5991 5992 5993 5994
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
    MSIQUERY *view;
5995 5996
    UINT rc;
    
5997 5998 5999
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szPublishComponents);

6000
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6001 6002 6003 6004 6005 6006 6007
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
    msiobj_release(&view->hdr);
    return rc;
}
6008

6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025
static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
{
    static const WCHAR szInstallerComponents[] = {
        'S','o','f','t','w','a','r','e','\\',
        'M','i','c','r','o','s','o','f','t','\\',
        'I','n','s','t','a','l','l','e','r','\\',
        'C','o','m','p','o','n','e','n','t','s','\\',0};

    MSIPACKAGE *package = param;
    LPCWSTR compgroupid, component, feature, qualifier;
    MSICOMPONENT *comp;
    MSIFEATURE *feat;
    MSIRECORD *uirow;
    WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
    LONG res;

    feature = MSI_RecordGetString( rec, 5 );
6026
    feat = msi_get_loaded_feature( package, feature );
6027 6028 6029
    if (!feat)
        return ERROR_SUCCESS;

6030 6031
    feat->Action = msi_get_feature_action( package, feat );
    if (feat->Action != INSTALLSTATE_ABSENT)
6032
    {
6033
        TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
6034 6035 6036 6037
        return ERROR_SUCCESS;
    }

    component = MSI_RecordGetString( rec, 3 );
6038
    comp = msi_get_loaded_component( package, component );
6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057
    if (!comp)
        return ERROR_SUCCESS;

    compgroupid = MSI_RecordGetString( rec, 1 );
    qualifier = MSI_RecordGetString( rec, 2 );

    squash_guid( compgroupid, squashed );
    strcpyW( keypath, szInstallerComponents );
    strcatW( keypath, squashed );

    res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
    if (res != ERROR_SUCCESS)
    {
        WARN("Unable to delete component key %d\n", res);
    }

    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, compgroupid );
    MSI_RecordSetStringW( uirow, 2, qualifier );
6058
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6059 6060 6061 6062 6063 6064 6065
    msiobj_release( &uirow->hdr );

    return ERROR_SUCCESS;
}

static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
{
6066 6067 6068
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
6069
    MSIQUERY *view;
6070
    UINT rc;
6071

6072 6073 6074
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szUnpublishComponents);

6075 6076 6077 6078 6079 6080 6081 6082 6083
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
    msiobj_release( &view->hdr );
    return rc;
}

6084 6085
static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
{
6086 6087 6088 6089
    static const WCHAR query[] =
        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
         '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
         '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
6090
    MSIPACKAGE *package = param;
6091
    MSICOMPONENT *component;
6092 6093
    MSIRECORD *row;
    MSIFILE *file;
6094
    SC_HANDLE hscm = NULL, service = NULL;
6095 6096
    LPCWSTR comp, key;
    LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
6097 6098
    LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
    DWORD serv_type, start_type, err_control;
6099
    BOOL is_vital;
6100
    SERVICE_DESCRIPTIONW sd = {NULL};
6101
    UINT ret = ERROR_SUCCESS;
6102

6103
    comp = MSI_RecordGetString( rec, 12 );
6104
    component = msi_get_loaded_component( package, comp );
6105 6106 6107 6108 6109
    if (!component)
    {
        WARN("service component not found\n");
        goto done;
    }
6110 6111
    component->Action = msi_get_component_action( package, component );
    if (component->Action != INSTALLSTATE_LOCAL)
6112
    {
6113
        TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
6114 6115
        goto done;
    }
6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126
    hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
    if (!hscm)
    {
        ERR("Failed to open the SC Manager!\n");
        goto done;
    }

    start_type = MSI_RecordGetInteger(rec, 5);
    if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
        goto done;

6127 6128
    deformat_string(package, MSI_RecordGetString(rec, 2), &name);
    deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
6129 6130
    serv_type = MSI_RecordGetInteger(rec, 4);
    err_control = MSI_RecordGetInteger(rec, 6);
6131
    deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
6132
    deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
6133 6134
    deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
    deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
6135
    deformat_string(package, MSI_RecordGetString(rec, 11), &args);
6136
    deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
6137

6138 6139 6140 6141 6142 6143 6144
    /* Should the complete install fail if CreateService fails? */
    is_vital = (err_control & msidbServiceInstallErrorControlVital);

    /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
       CreateService (under Windows) would fail if not. */
    err_control &= ~msidbServiceInstallErrorControlVital;

6145 6146 6147 6148
    /* fetch the service path */
    row = MSI_QueryGetRecord(package->db, query, comp);
    if (!row)
    {
6149
        ERR("Query failed\n");
6150 6151
        goto done;
    }
6152 6153 6154 6155 6156
    if (!(key = MSI_RecordGetString(row, 6)))
    {
        msiobj_release(&row->hdr);
        goto done;
    }
6157
    file = msi_get_loaded_file(package, key);
6158
    msiobj_release(&row->hdr);
6159 6160 6161 6162 6163 6164
    if (!file)
    {
        ERR("Failed to load the service file\n");
        goto done;
    }

6165 6166 6167 6168 6169
    if (!args || !args[0]) image_path = file->TargetPath;
    else
    {
        int len = strlenW(file->TargetPath) + strlenW(args) + 2;
        if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6170 6171 6172 6173
        {
            ret = ERROR_OUTOFMEMORY;
            goto done;
        }
6174 6175 6176 6177 6178

        strcpyW(image_path, file->TargetPath);
        strcatW(image_path, szSpace);
        strcatW(image_path, args);
    }
6179
    service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6180 6181 6182
                             start_type, err_control, image_path, load_order,
                             NULL, depends, serv_name, pass);

6183 6184 6185
    if (!service)
    {
        if (GetLastError() != ERROR_SERVICE_EXISTS)
6186 6187 6188 6189 6190 6191
        {
            WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
            if (is_vital)
                ret = ERROR_INSTALL_FAILURE;

        }
6192
    }
6193 6194 6195 6196 6197
    else if (sd.lpDescription)
    {
        if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
            WARN("failed to set service description %u\n", GetLastError());
    }
6198

6199
    if (image_path != file->TargetPath) msi_free(image_path);
6200
done:
6201 6202
    if (service) CloseServiceHandle(service);
    if (hscm) CloseServiceHandle(hscm);
6203 6204
    msi_free(name);
    msi_free(disp);
6205
    msi_free(sd.lpDescription);
6206 6207 6208
    msi_free(load_order);
    msi_free(serv_name);
    msi_free(pass);
6209
    msi_free(depends);
6210
    msi_free(args);
6211

6212
    return ret;
6213 6214 6215 6216
}

static UINT ACTION_InstallServices( MSIPACKAGE *package )
{
6217 6218 6219 6220
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
    MSIQUERY *view;
6221 6222
    UINT rc;
    
6223 6224 6225
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szInstallServices);

6226
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6227 6228 6229 6230 6231 6232 6233 6234
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
    msiobj_release(&view->hdr);
    return rc;
}

6235
/* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6236
static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6237
{
6238
    LPCWSTR *vector, *temp_vector;
6239 6240 6241 6242 6243 6244
    LPWSTR p, q;
    DWORD sep_len;

    static const WCHAR separator[] = {'[','~',']',0};

    *numargs = 0;
6245
    sep_len = ARRAY_SIZE(separator) - 1;
6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263

    if (!args)
        return NULL;

    vector = msi_alloc(sizeof(LPWSTR));
    if (!vector)
        return NULL;

    p = args;
    do
    {
        (*numargs)++;
        vector[*numargs - 1] = p;

        if ((q = strstrW(p, separator)))
        {
            *q = '\0';

6264 6265 6266 6267
            temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
            if (!temp_vector)
            {
                msi_free(vector);
6268
                return NULL;
6269 6270
            }
            vector = temp_vector;
6271 6272 6273 6274 6275 6276 6277 6278 6279 6280

            p = q + sep_len;
        }
    } while (q);

    return vector;
}

static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
{
6281
    MSIPACKAGE *package = param;
6282
    MSICOMPONENT *comp;
6283
    MSIRECORD *uirow;
6284
    SC_HANDLE scm = NULL, service = NULL;
6285
    LPCWSTR component, *vector = NULL;
6286
    LPWSTR name, args, display_name = NULL;
6287
    DWORD event, numargs, len, wait, dummy;
6288
    UINT r = ERROR_FUNCTION_FAILED;
6289 6290
    SERVICE_STATUS_PROCESS status;
    ULONGLONG start_time;
6291

6292
    component = MSI_RecordGetString(rec, 6);
6293
    comp = msi_get_loaded_component(package, component);
6294 6295 6296
    if (!comp)
        return ERROR_SUCCESS;

6297 6298 6299
    event = MSI_RecordGetInteger( rec, 3 );
    deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );

6300
    comp->Action = msi_get_component_action( package, comp );
6301 6302
    if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
        !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6303
    {
6304 6305
        TRACE("not starting %s\n", debugstr_w(name));
        msi_free( name );
6306
        return ERROR_SUCCESS;
6307
    }
6308

6309
    deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6310
    wait = MSI_RecordGetInteger(rec, 5);
6311 6312 6313 6314 6315 6316 6317 6318

    scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
    if (!scm)
    {
        ERR("Failed to open the service control manager\n");
        goto done;
    }

6319 6320 6321 6322 6323 6324 6325 6326
    len = 0;
    if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
        GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
            GetServiceDisplayNameW( scm, name, display_name, &len );
    }

6327
    service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6328 6329
    if (!service)
    {
6330
        ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6331 6332 6333
        goto done;
    }

6334
    vector = msi_service_args_to_vector(args, &numargs);
6335

6336 6337
    if (!StartServiceW(service, numargs, vector) &&
        GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6338
    {
6339
        ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6340 6341 6342 6343
        goto done;
    }

    r = ERROR_SUCCESS;
6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370
    if (wait)
    {
        /* wait for at most 30 seconds for the service to be up and running */
        if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
            (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
        {
            TRACE("failed to query service status (%u)\n", GetLastError());
            goto done;
        }
        start_time = GetTickCount64();
        while (status.dwCurrentState == SERVICE_START_PENDING)
        {
            if (GetTickCount64() - start_time > 30000) break;
            Sleep(1000);
            if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
                (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
            {
                TRACE("failed to query service status (%u)\n", GetLastError());
                goto done;
            }
        }
        if (status.dwCurrentState != SERVICE_RUNNING)
        {
            WARN("service failed to start %u\n", status.dwCurrentState);
            r = ERROR_FUNCTION_FAILED;
        }
    }
6371 6372

done:
6373 6374 6375
    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, display_name );
    MSI_RecordSetStringW( uirow, 2, name );
6376
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6377 6378
    msiobj_release( &uirow->hdr );

6379 6380
    if (service) CloseServiceHandle(service);
    if (scm) CloseServiceHandle(scm);
6381

6382
    msi_free(name);
6383 6384
    msi_free(args);
    msi_free(vector);
6385
    msi_free(display_name);
6386 6387 6388 6389 6390 6391 6392
    return r;
}

static UINT ACTION_StartServices( MSIPACKAGE *package )
{
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6393 6394 6395
        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
    MSIQUERY *view;
    UINT rc;
6396

6397 6398 6399
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szStartServices);

6400 6401 6402 6403 6404 6405 6406 6407 6408
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
    msiobj_release(&view->hdr);
    return rc;
}

6409 6410 6411 6412 6413 6414
static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
{
    DWORD i, needed, count;
    ENUM_SERVICE_STATUSW *dependencies;
    SERVICE_STATUS ss;
    SC_HANDLE depserv;
6415
    BOOL stopped, ret = FALSE;
6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429

    if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
                               0, &needed, &count))
        return TRUE;

    if (GetLastError() != ERROR_MORE_DATA)
        return FALSE;

    dependencies = msi_alloc(needed);
    if (!dependencies)
        return FALSE;

    if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
                                needed, &needed, &count))
6430
        goto done;
6431 6432 6433 6434 6435 6436

    for (i = 0; i < count; i++)
    {
        depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
                               SERVICE_STOP | SERVICE_QUERY_STATUS);
        if (!depserv)
6437
            goto done;
6438

6439 6440 6441 6442
        stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
        CloseServiceHandle(depserv);
        if (!stopped)
            goto done;
6443 6444
    }

6445
    ret = TRUE;
6446

6447
done:
6448
    msi_free(dependencies);
6449
    return ret;
6450 6451
}

6452
static UINT stop_service( LPCWSTR name )
6453
{
6454
    SC_HANDLE scm = NULL, service = NULL;
6455 6456
    SERVICE_STATUS status;
    SERVICE_STATUS_PROCESS ssp;
6457
    DWORD needed;
6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471

    scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm)
    {
        WARN("Failed to open the SCM: %d\n", GetLastError());
        goto done;
    }

    service = OpenServiceW(scm, name,
                           SERVICE_STOP |
                           SERVICE_QUERY_STATUS |
                           SERVICE_ENUMERATE_DEPENDENTS);
    if (!service)
    {
6472
        WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6473 6474 6475 6476 6477 6478
        goto done;
    }

    if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
                              sizeof(SERVICE_STATUS_PROCESS), &needed))
    {
6479
        WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6480 6481 6482 6483 6484 6485 6486 6487
        goto done;
    }

    if (ssp.dwCurrentState == SERVICE_STOPPED)
        goto done;

    stop_service_dependents(scm, service);

James Hawkins's avatar
James Hawkins committed
6488
    if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6489 6490 6491
        WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());

done:
6492 6493
    if (service) CloseServiceHandle(service);
    if (scm) CloseServiceHandle(scm);
6494 6495 6496 6497 6498 6499 6500 6501

    return ERROR_SUCCESS;
}

static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
{
    MSIPACKAGE *package = param;
    MSICOMPONENT *comp;
6502
    MSIRECORD *uirow;
6503
    LPCWSTR component;
6504
    WCHAR *name, *display_name = NULL;
6505 6506
    DWORD event, len;
    SC_HANDLE scm;
6507

6508
    component = MSI_RecordGetString( rec, 6 );
6509
    comp = msi_get_loaded_component( package, component );
6510
    if (!comp)
6511 6512
        return ERROR_SUCCESS;

6513 6514 6515
    event = MSI_RecordGetInteger( rec, 3 );
    deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );

6516
    comp->Action = msi_get_component_action( package, comp );
6517 6518
    if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
        !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6519
    {
6520 6521
        TRACE("not stopping %s\n", debugstr_w(name));
        msi_free( name );
6522 6523 6524
        return ERROR_SUCCESS;
    }

6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540
    scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
    if (!scm)
    {
        ERR("Failed to open the service control manager\n");
        goto done;
    }

    len = 0;
    if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
        GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
            GetServiceDisplayNameW( scm, name, display_name, &len );
    }
    CloseServiceHandle( scm );

6541
    stop_service( name );
6542

6543 6544 6545 6546
done:
    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, display_name );
    MSI_RecordSetStringW( uirow, 2, name );
6547
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6548 6549 6550 6551
    msiobj_release( &uirow->hdr );

    msi_free( name );
    msi_free( display_name );
6552 6553 6554 6555 6556 6557 6558
    return ERROR_SUCCESS;
}

static UINT ACTION_StopServices( MSIPACKAGE *package )
{
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6559 6560 6561
        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
    MSIQUERY *view;
    UINT rc;
6562

6563 6564 6565
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szStopServices);

6566 6567 6568 6569 6570 6571 6572 6573 6574
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
    msiobj_release(&view->hdr);
    return rc;
}

6575 6576 6577 6578
static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
{
    MSIPACKAGE *package = param;
    MSICOMPONENT *comp;
6579 6580 6581
    MSIRECORD *uirow;
    LPWSTR name = NULL, display_name = NULL;
    DWORD event, len;
6582 6583
    SC_HANDLE scm = NULL, service = NULL;

6584
    comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6585 6586 6587
    if (!comp)
        return ERROR_SUCCESS;

6588 6589 6590
    event = MSI_RecordGetInteger( rec, 3 );
    deformat_string( package, MSI_RecordGetString(rec, 2), &name );

6591
    comp->Action = msi_get_component_action( package, comp );
6592 6593
    if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
        !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6594
    {
6595 6596
        TRACE("service %s not scheduled for removal\n", debugstr_w(name));
        msi_free( name );
6597
        return ERROR_SUCCESS;
6598
    }
6599 6600 6601 6602 6603 6604 6605 6606 6607
    stop_service( name );

    scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
    if (!scm)
    {
        WARN("Failed to open the SCM: %d\n", GetLastError());
        goto done;
    }

6608 6609 6610 6611 6612 6613 6614 6615
    len = 0;
    if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
        GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
            GetServiceDisplayNameW( scm, name, display_name, &len );
    }

6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626
    service = OpenServiceW( scm, name, DELETE );
    if (!service)
    {
        WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
        goto done;
    }

    if (!DeleteService( service ))
        WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());

done:
6627 6628 6629
    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, display_name );
    MSI_RecordSetStringW( uirow, 2, name );
6630
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6631 6632
    msiobj_release( &uirow->hdr );

6633 6634
    if (service) CloseServiceHandle( service );
    if (scm) CloseServiceHandle( scm );
6635
    msi_free( name );
6636
    msi_free( display_name );
6637 6638 6639 6640 6641 6642 6643 6644

    return ERROR_SUCCESS;
}

static UINT ACTION_DeleteServices( MSIPACKAGE *package )
{
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6645 6646 6647
        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
    MSIQUERY *view;
    UINT rc;
6648

6649 6650 6651
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szDeleteServices);

6652 6653 6654 6655 6656 6657 6658 6659 6660
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
    msiobj_release( &view->hdr );
    return rc;
}

6661 6662
static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
{
6663
    MSIPACKAGE *package = param;
6664 6665
    LPWSTR driver, driver_path, ptr;
    WCHAR outpath[MAX_PATH];
6666
    MSIFILE *driver_file = NULL, *setup_file = NULL;
6667
    MSICOMPONENT *comp;
6668
    MSIRECORD *uirow;
6669
    LPCWSTR desc, file_key, component;
6670 6671 6672 6673 6674 6675 6676 6677 6678 6679
    DWORD len, usage;
    UINT r = ERROR_SUCCESS;

    static const WCHAR driver_fmt[] = {
        'D','r','i','v','e','r','=','%','s',0};
    static const WCHAR setup_fmt[] = {
        'S','e','t','u','p','=','%','s',0};
    static const WCHAR usage_fmt[] = {
        'F','i','l','e','U','s','a','g','e','=','1',0};

6680
    component = MSI_RecordGetString( rec, 2 );
6681
    comp = msi_get_loaded_component( package, component );
6682 6683 6684
    if (!comp)
        return ERROR_SUCCESS;

6685 6686
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
6687
    {
6688
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6689 6690
        return ERROR_SUCCESS;
    }
6691 6692
    desc = MSI_RecordGetString(rec, 3);

6693
    file_key = MSI_RecordGetString( rec, 4 );
6694
    if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6695 6696

    file_key = MSI_RecordGetString( rec, 5 );
6697
    if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6698

6699
    if (!driver_file)
6700 6701 6702 6703 6704
    {
        ERR("ODBC Driver entry not found!\n");
        return ERROR_FUNCTION_FAILED;
    }

6705 6706 6707
    len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
    if (setup_file)
        len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6708
    len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6709

6710 6711 6712 6713 6714 6715 6716 6717
    driver = msi_alloc(len * sizeof(WCHAR));
    if (!driver)
        return ERROR_OUTOFMEMORY;

    ptr = driver;
    lstrcpyW(ptr, desc);
    ptr += lstrlenW(ptr) + 1;

6718 6719
    len = sprintfW(ptr, driver_fmt, driver_file->FileName);
    ptr += len + 1;
6720

6721 6722
    if (setup_file)
    {
6723 6724
        len = sprintfW(ptr, setup_fmt, setup_file->FileName);
        ptr += len + 1;
6725
    }
6726 6727 6728 6729 6730

    lstrcpyW(ptr, usage_fmt);
    ptr += lstrlenW(ptr) + 1;
    *ptr = '\0';

6731 6732 6733 6734 6735
    if (!driver_file->TargetPath)
    {
        const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
        driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
    }
6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746
    driver_path = strdupW(driver_file->TargetPath);
    ptr = strrchrW(driver_path, '\\');
    if (ptr) *ptr = '\0';

    if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
                             NULL, ODBC_INSTALL_COMPLETE, &usage))
    {
        ERR("Failed to install SQL driver!\n");
        r = ERROR_FUNCTION_FAILED;
    }

6747 6748 6749
    uirow = MSI_CreateRecord( 5 );
    MSI_RecordSetStringW( uirow, 1, desc );
    MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6750
    MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6751
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6752 6753
    msiobj_release( &uirow->hdr );

6754 6755 6756 6757 6758 6759
    msi_free(driver);
    msi_free(driver_path);

    return r;
}

6760 6761
static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
{
6762
    MSIPACKAGE *package = param;
6763 6764
    LPWSTR translator, translator_path, ptr;
    WCHAR outpath[MAX_PATH];
6765
    MSIFILE *translator_file = NULL, *setup_file = NULL;
6766
    MSICOMPONENT *comp;
6767
    MSIRECORD *uirow;
6768
    LPCWSTR desc, file_key, component;
6769 6770 6771 6772 6773 6774 6775 6776
    DWORD len, usage;
    UINT r = ERROR_SUCCESS;

    static const WCHAR translator_fmt[] = {
        'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
    static const WCHAR setup_fmt[] = {
        'S','e','t','u','p','=','%','s',0};

6777
    component = MSI_RecordGetString( rec, 2 );
6778
    comp = msi_get_loaded_component( package, component );
6779 6780 6781
    if (!comp)
        return ERROR_SUCCESS;

6782 6783
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
6784
    {
6785
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6786 6787
        return ERROR_SUCCESS;
    }
6788 6789
    desc = MSI_RecordGetString(rec, 3);

6790
    file_key = MSI_RecordGetString( rec, 4 );
6791
    if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6792 6793

    file_key = MSI_RecordGetString( rec, 5 );
6794
    if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6795

6796
    if (!translator_file)
6797 6798 6799 6800 6801
    {
        ERR("ODBC Translator entry not found!\n");
        return ERROR_FUNCTION_FAILED;
    }

6802
    len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6803 6804 6805
    if (setup_file)
        len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);

6806 6807 6808 6809 6810 6811 6812 6813
    translator = msi_alloc(len * sizeof(WCHAR));
    if (!translator)
        return ERROR_OUTOFMEMORY;

    ptr = translator;
    lstrcpyW(ptr, desc);
    ptr += lstrlenW(ptr) + 1;

6814 6815
    len = sprintfW(ptr, translator_fmt, translator_file->FileName);
    ptr += len + 1;
6816

6817 6818
    if (setup_file)
    {
6819 6820
        len = sprintfW(ptr, setup_fmt, setup_file->FileName);
        ptr += len + 1;
6821
    }
6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834
    *ptr = '\0';

    translator_path = strdupW(translator_file->TargetPath);
    ptr = strrchrW(translator_path, '\\');
    if (ptr) *ptr = '\0';

    if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
                                 NULL, ODBC_INSTALL_COMPLETE, &usage))
    {
        ERR("Failed to install SQL translator!\n");
        r = ERROR_FUNCTION_FAILED;
    }

6835 6836 6837
    uirow = MSI_CreateRecord( 5 );
    MSI_RecordSetStringW( uirow, 1, desc );
    MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6838
    MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6839
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6840 6841
    msiobj_release( &uirow->hdr );

6842 6843 6844 6845 6846 6847
    msi_free(translator);
    msi_free(translator_path);

    return r;
}

6848 6849
static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
{
6850
    MSIPACKAGE *package = param;
6851
    MSICOMPONENT *comp;
6852
    LPWSTR attrs;
6853
    LPCWSTR desc, driver, component;
6854 6855 6856 6857
    WORD request = ODBC_ADD_SYS_DSN;
    INT registration;
    DWORD len;
    UINT r = ERROR_SUCCESS;
6858
    MSIRECORD *uirow;
6859 6860 6861 6862

    static const WCHAR attrs_fmt[] = {
        'D','S','N','=','%','s',0 };

6863
    component = MSI_RecordGetString( rec, 2 );
6864
    comp = msi_get_loaded_component( package, component );
6865 6866 6867
    if (!comp)
        return ERROR_SUCCESS;

6868 6869
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
6870
    {
6871
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6872 6873 6874
        return ERROR_SUCCESS;
    }

6875 6876 6877 6878 6879 6880 6881
    desc = MSI_RecordGetString(rec, 3);
    driver = MSI_RecordGetString(rec, 4);
    registration = MSI_RecordGetInteger(rec, 5);

    if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
    else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;

6882
    len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6883 6884 6885 6886
    attrs = msi_alloc(len * sizeof(WCHAR));
    if (!attrs)
        return ERROR_OUTOFMEMORY;

6887 6888
    len = sprintfW(attrs, attrs_fmt, desc);
    attrs[len + 1] = 0;
6889 6890 6891 6892 6893 6894 6895

    if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
    {
        ERR("Failed to install SQL data source!\n");
        r = ERROR_FUNCTION_FAILED;
    }

6896 6897 6898 6899
    uirow = MSI_CreateRecord( 5 );
    MSI_RecordSetStringW( uirow, 1, desc );
    MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
    MSI_RecordSetInteger( uirow, 3, request );
6900
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6901 6902
    msiobj_release( &uirow->hdr );

6903 6904 6905 6906 6907
    msi_free(attrs);

    return r;
}

6908 6909
static UINT ACTION_InstallODBC( MSIPACKAGE *package )
{
6910
    static const WCHAR driver_query[] = {
6911
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6912
        'O','D','B','C','D','r','i','v','e','r',0};
6913 6914
    static const WCHAR translator_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6915
        'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6916 6917
    static const WCHAR source_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6918 6919 6920
        'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
    MSIQUERY *view;
    UINT rc;
6921

6922 6923 6924
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szInstallODBC);

6925
    rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6926 6927 6928 6929 6930 6931 6932
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
        msiobj_release(&view->hdr);
        if (rc != ERROR_SUCCESS)
            return rc;
    }
6933
    rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6934 6935 6936 6937 6938 6939 6940
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
        msiobj_release(&view->hdr);
        if (rc != ERROR_SUCCESS)
            return rc;
    }
6941
    rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6942 6943 6944 6945 6946 6947 6948 6949
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
        msiobj_release(&view->hdr);
        if (rc != ERROR_SUCCESS)
            return rc;
    }
    return ERROR_SUCCESS;
6950 6951
}

6952 6953
static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
{
6954
    MSIPACKAGE *package = param;
6955
    MSICOMPONENT *comp;
6956
    MSIRECORD *uirow;
6957
    DWORD usage;
6958 6959 6960
    LPCWSTR desc, component;

    component = MSI_RecordGetString( rec, 2 );
6961
    comp = msi_get_loaded_component( package, component );
6962 6963 6964
    if (!comp)
        return ERROR_SUCCESS;

6965 6966
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
6967
    {
6968
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6969 6970
        return ERROR_SUCCESS;
    }
6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981

    desc = MSI_RecordGetString( rec, 3 );
    if (!SQLRemoveDriverW( desc, FALSE, &usage ))
    {
        WARN("Failed to remove ODBC driver\n");
    }
    else if (!usage)
    {
        FIXME("Usage count reached 0\n");
    }

6982 6983 6984
    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, desc );
    MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6985
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6986 6987
    msiobj_release( &uirow->hdr );

6988 6989 6990 6991 6992
    return ERROR_SUCCESS;
}

static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
{
6993
    MSIPACKAGE *package = param;
6994
    MSICOMPONENT *comp;
6995
    MSIRECORD *uirow;
6996
    DWORD usage;
6997 6998 6999
    LPCWSTR desc, component;

    component = MSI_RecordGetString( rec, 2 );
7000
    comp = msi_get_loaded_component( package, component );
7001 7002 7003
    if (!comp)
        return ERROR_SUCCESS;

7004 7005
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
7006
    {
7007
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7008 7009
        return ERROR_SUCCESS;
    }
7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020

    desc = MSI_RecordGetString( rec, 3 );
    if (!SQLRemoveTranslatorW( desc, &usage ))
    {
        WARN("Failed to remove ODBC translator\n");
    }
    else if (!usage)
    {
        FIXME("Usage count reached 0\n");
    }

7021 7022 7023
    uirow = MSI_CreateRecord( 2 );
    MSI_RecordSetStringW( uirow, 1, desc );
    MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7024
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7025 7026
    msiobj_release( &uirow->hdr );

7027 7028 7029 7030 7031
    return ERROR_SUCCESS;
}

static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
{
7032
    MSIPACKAGE *package = param;
7033
    MSICOMPONENT *comp;
7034
    MSIRECORD *uirow;
7035
    LPWSTR attrs;
7036
    LPCWSTR desc, driver, component;
7037 7038 7039 7040 7041 7042 7043
    WORD request = ODBC_REMOVE_SYS_DSN;
    INT registration;
    DWORD len;

    static const WCHAR attrs_fmt[] = {
        'D','S','N','=','%','s',0 };

7044
    component = MSI_RecordGetString( rec, 2 );
7045
    comp = msi_get_loaded_component( package, component );
7046 7047 7048
    if (!comp)
        return ERROR_SUCCESS;

7049 7050
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
7051
    {
7052
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7053 7054 7055
        return ERROR_SUCCESS;
    }

7056 7057 7058 7059 7060 7061 7062
    desc = MSI_RecordGetString( rec, 3 );
    driver = MSI_RecordGetString( rec, 4 );
    registration = MSI_RecordGetInteger( rec, 5 );

    if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
    else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;

7063
    len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078
    attrs = msi_alloc( len * sizeof(WCHAR) );
    if (!attrs)
        return ERROR_OUTOFMEMORY;

    FIXME("Use ODBCSourceAttribute table\n");

    len = sprintfW( attrs, attrs_fmt, desc );
    attrs[len + 1] = 0;

    if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
    {
        WARN("Failed to remove ODBC data source\n");
    }
    msi_free( attrs );

7079 7080 7081 7082
    uirow = MSI_CreateRecord( 3 );
    MSI_RecordSetStringW( uirow, 1, desc );
    MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
    MSI_RecordSetInteger( uirow, 3, request );
7083
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7084 7085
    msiobj_release( &uirow->hdr );

7086 7087 7088 7089 7090 7091 7092
    return ERROR_SUCCESS;
}

static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
{
    static const WCHAR driver_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7093
        'O','D','B','C','D','r','i','v','e','r',0};
7094 7095
    static const WCHAR translator_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7096
        'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7097 7098
    static const WCHAR source_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7099 7100 7101
        'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
    MSIQUERY *view;
    UINT rc;
7102

7103 7104 7105
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveODBC);

7106
    rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7107 7108 7109 7110 7111 7112 7113
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
        msiobj_release( &view->hdr );
        if (rc != ERROR_SUCCESS)
            return rc;
    }
7114
    rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7115 7116 7117 7118 7119 7120 7121
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
        msiobj_release( &view->hdr );
        if (rc != ERROR_SUCCESS)
            return rc;
    }
7122
    rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
7123 7124 7125 7126 7127 7128 7129 7130
    if (rc == ERROR_SUCCESS)
    {
        rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
        msiobj_release( &view->hdr );
        if (rc != ERROR_SUCCESS)
            return rc;
    }
    return ERROR_SUCCESS;
7131 7132
}

7133 7134 7135
#define ENV_ACT_SETALWAYS   0x1
#define ENV_ACT_SETABSENT   0x2
#define ENV_ACT_REMOVE      0x4
7136
#define ENV_ACT_REMOVEMATCH 0x8
7137 7138 7139 7140

#define ENV_MOD_MACHINE     0x20000000
#define ENV_MOD_APPEND      0x40000000
#define ENV_MOD_PREFIX      0x80000000
7141 7142 7143 7144
#define ENV_MOD_MASK        0xC0000000

#define check_flag_combo(x, y) ((x) & ~(y)) == (y)

7145
static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
7146 7147 7148 7149
{
    LPCWSTR cptr = *name;

    static const WCHAR prefix[] = {'[','~',']',0};
7150
    static const int prefix_len = 3;
7151 7152

    *flags = 0;
7153
    while (*cptr)
7154
    {
7155
        if (*cptr == '=')
7156
            *flags |= ENV_ACT_SETALWAYS;
7157
        else if (*cptr == '+')
7158
            *flags |= ENV_ACT_SETABSENT;
7159
        else if (*cptr == '-')
7160
            *flags |= ENV_ACT_REMOVE;
7161
        else if (*cptr == '!')
7162
            *flags |= ENV_ACT_REMOVEMATCH;
7163
        else if (*cptr == '*')
7164
            *flags |= ENV_MOD_MACHINE;
7165
        else
7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177
            break;

        cptr++;
        (*name)++;
    }

    if (!*cptr)
    {
        ERR("Missing environment variable\n");
        return ERROR_FUNCTION_FAILED;
    }

7178
    if (*value)
7179
    {
7180 7181
        LPCWSTR ptr = *value;
        if (!strncmpW(ptr, prefix, prefix_len))
7182
        {
7183 7184 7185 7186 7187 7188 7189 7190 7191
            if (ptr[prefix_len] == szSemiColon[0])
            {
                *flags |= ENV_MOD_APPEND;
                *value += lstrlenW(prefix);
            }
            else
            {
                *value = NULL;
            }
7192 7193 7194 7195
        }
        else if (lstrlenW(*value) >= prefix_len)
        {
            ptr += lstrlenW(ptr) - prefix_len;
7196
            if (!strcmpW( ptr, prefix ))
7197
            {
7198 7199 7200 7201 7202 7203 7204 7205 7206
                if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
                {
                    *flags |= ENV_MOD_PREFIX;
                    /* the "[~]" will be removed by deformat_string */;
                }
                else
                {
                    *value = NULL;
                }
7207
            }
7208 7209 7210
        }
    }

7211
    if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7212 7213 7214 7215 7216 7217 7218 7219
        check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
        check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
        check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
    {
        ERR("Invalid flags: %08x\n", *flags);
        return ERROR_FUNCTION_FAILED;
    }

7220 7221 7222
    if (!*flags)
        *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;

7223 7224
    return ERROR_SUCCESS;
}
7225

7226
static UINT open_env_key( DWORD flags, HKEY *key )
7227
{
7228 7229 7230
    static const WCHAR user_env[] =
        {'E','n','v','i','r','o','n','m','e','n','t',0};
    static const WCHAR machine_env[] =
7231 7232 7233 7234 7235
        {'S','y','s','t','e','m','\\',
         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
         'C','o','n','t','r','o','l','\\',
         'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
         'E','n','v','i','r','o','n','m','e','n','t',0};
7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264
    const WCHAR *env;
    HKEY root;
    LONG res;

    if (flags & ENV_MOD_MACHINE)
    {
        env = machine_env;
        root = HKEY_LOCAL_MACHINE;
    }
    else
    {
        env = user_env;
        root = HKEY_CURRENT_USER;
    }

    res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
    if (res != ERROR_SUCCESS)
    {
        WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
        return ERROR_FUNCTION_FAILED;
    }

    return ERROR_SUCCESS;
}

static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPCWSTR name, value, component;
7265 7266
    WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
    DWORD flags, type, size, len, len_value = 0;
7267
    UINT res;
7268
    HKEY env = NULL;
7269 7270
    MSICOMPONENT *comp;
    MSIRECORD *uirow;
7271
    int action = 0, found = 0;
7272

7273
    component = MSI_RecordGetString(rec, 4);
7274
    comp = msi_get_loaded_component(package, component);
7275 7276 7277
    if (!comp)
        return ERROR_SUCCESS;

7278 7279
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
7280
    {
7281
        TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7282 7283
        return ERROR_SUCCESS;
    }
7284 7285 7286
    name = MSI_RecordGetString(rec, 2);
    value = MSI_RecordGetString(rec, 3);

7287 7288
    TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));

7289
    res = env_parse_flags(&name, &value, &flags);
7290
    if (res != ERROR_SUCCESS || !value)
7291
       goto done;
7292

7293
    if (value && !deformat_string(package, value, &deformatted))
7294 7295 7296 7297 7298
    {
        res = ERROR_OUTOFMEMORY;
        goto done;
    }

7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312
    if ((value = deformatted))
    {
        if (flags & ENV_MOD_PREFIX)
        {
            p = strrchrW( value, ';' );
            len_value = p - value;
        }
        else if (flags & ENV_MOD_APPEND)
        {
            value = strchrW( value, ';' ) + 1;
            len_value = strlenW( value );
        }
        else len_value = strlenW( value );
    }
7313

7314
    res = open_env_key( flags, &env );
7315
    if (res != ERROR_SUCCESS)
7316
        goto done;
7317

7318 7319
    if (flags & ENV_MOD_MACHINE)
        action |= 0x20000000;
7320 7321

    size = 0;
7322
    type = REG_SZ;
7323 7324
    res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
    if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7325
        (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7326
        goto done;
7327

7328 7329
    if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
    {
7330 7331
        action = 0x2;

7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346
        /* Nothing to do. */
        if (!value)
        {
            res = ERROR_SUCCESS;
            goto done;
        }
        size = (lstrlenW(value) + 1) * sizeof(WCHAR);
        newval = strdupW(value);
        if (!newval)
        {
            res = ERROR_OUTOFMEMORY;
            goto done;
        }
    }
    else
7347
    {
7348 7349
        action = 0x1;

7350 7351
        /* Contrary to MSDN, +-variable to [~];path works */
        if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7352 7353 7354 7355 7356
        {
            res = ERROR_SUCCESS;
            goto done;
        }

7357
        if (!(p = q = data = msi_alloc( size )))
7358
        {
7359
            msi_free(deformatted);
7360 7361
            RegCloseKey(env);
            return ERROR_OUTOFMEMORY;
7362 7363
        }

7364
        res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7365 7366 7367
        if (res != ERROR_SUCCESS)
            goto done;

7368
        if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7369
        {
7370 7371 7372 7373
            action = 0x4;
            res = RegDeleteValueW(env, name);
            if (res != ERROR_SUCCESS)
                WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7374
            goto done;
7375 7376
        }

7377
        for (;;)
7378
        {
7379 7380 7381 7382 7383 7384 7385 7386 7387 7388
            while (*q && *q != ';') q++;
            len = q - p;
            if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
                (!p[len] || p[len] == ';'))
            {
                found = 1;
                break;
            }
            if (!*q) break;
            p = ++q;
7389 7390
        }

7391 7392 7393 7394 7395 7396 7397 7398
        if (found)
        {
            TRACE("string already set\n");
            goto done;
        }

        size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
        if (!(p = newval = msi_alloc( size )))
7399 7400
        {
            res = ERROR_OUTOFMEMORY;
7401
            goto done;
7402
        }
7403

7404
        if (flags & ENV_MOD_PREFIX)
7405
        {
7406 7407 7408
            memcpy( newval, value, len_value * sizeof(WCHAR) );
            newval[len_value] = ';';
            p = newval + len_value + 1;
7409
            action |= 0x80000000;
7410
        }
7411

7412
        strcpyW( p, data );
7413

7414
        if (flags & ENV_MOD_APPEND)
7415
        {
7416 7417 7418
            p += strlenW( data );
            *p++ = ';';
            memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7419
            action |= 0x40000000;
7420 7421
        }
    }
7422
    TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7423
    res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7424 7425 7426 7427
    if (res)
    {
        WARN("Failed to set %s to %s (%d)\n",  debugstr_w(name), debugstr_w(newval), res);
    }
7428 7429

done:
7430 7431 7432 7433
    uirow = MSI_CreateRecord( 3 );
    MSI_RecordSetStringW( uirow, 1, name );
    MSI_RecordSetStringW( uirow, 2, newval );
    MSI_RecordSetInteger( uirow, 3, action );
7434
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7435 7436
    msiobj_release( &uirow->hdr );

7437
    if (env) RegCloseKey(env);
7438 7439 7440
    msi_free(deformatted);
    msi_free(data);
    msi_free(newval);
7441 7442 7443 7444 7445
    return res;
}

static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
{
7446 7447 7448 7449
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
    MSIQUERY *view;
7450
    UINT rc;
7451

7452 7453 7454
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szWriteEnvironmentStrings);

7455
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7456 7457 7458 7459 7460 7461 7462 7463
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
    msiobj_release(&view->hdr);
    return rc;
}

7464 7465 7466 7467
static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPCWSTR name, value, component;
7468 7469
    WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
    DWORD flags, type, size, len, len_value = 0, len_new_value;
7470 7471 7472 7473 7474 7475 7476 7477
    HKEY env;
    MSICOMPONENT *comp;
    MSIRECORD *uirow;
    int action = 0;
    LONG res;
    UINT r;

    component = MSI_RecordGetString( rec, 4 );
7478
    comp = msi_get_loaded_component( package, component );
7479 7480 7481
    if (!comp)
        return ERROR_SUCCESS;

7482 7483
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
7484
    {
7485
        TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505
        return ERROR_SUCCESS;
    }
    name = MSI_RecordGetString( rec, 2 );
    value = MSI_RecordGetString( rec, 3 );

    TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));

    r = env_parse_flags( &name, &value, &flags );
    if (r != ERROR_SUCCESS)
       return r;

    if (!(flags & ENV_ACT_REMOVE))
    {
        TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
        return ERROR_SUCCESS;
    }

    if (value && !deformat_string( package, value, &deformatted ))
        return ERROR_OUTOFMEMORY;

7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519
    if ((value = deformatted))
    {
        if (flags & ENV_MOD_PREFIX)
        {
            p = strchrW( value, ';' );
            len_value = p - value;
        }
        else if (flags & ENV_MOD_APPEND)
        {
            value = strchrW( value, ';' ) + 1;
            len_value = strlenW( value );
        }
        else len_value = strlenW( value );
    }
7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530

    r = open_env_key( flags, &env );
    if (r != ERROR_SUCCESS)
    {
        r = ERROR_SUCCESS;
        goto done;
    }

    if (flags & ENV_MOD_MACHINE)
        action |= 0x20000000;

7531 7532 7533 7534 7535 7536 7537
    size = 0;
    type = REG_SZ;
    res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
    if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
        goto done;

    if (!(new_value = msi_alloc( size ))) goto done;
7538

7539
    res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7540
    if (res != ERROR_SUCCESS)
7541 7542 7543 7544 7545
        goto done;

    len_new_value = size / sizeof(WCHAR) - 1;
    p = q = new_value;
    for (;;)
7546
    {
7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572
        while (*q && *q != ';') q++;
        len = q - p;
        if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
        {
            if (*q == ';') q++;
            memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
            break;
        }
        if (!*q) break;
        p = ++q;
    }

    if (!new_value[0] || !value)
    {
        TRACE("removing %s\n", debugstr_w(name));
        res = RegDeleteValueW( env, name );
        if (res != ERROR_SUCCESS)
            WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
    }
    else
    {
        TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
        size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
        res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
        if (res != ERROR_SUCCESS)
            WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7573 7574 7575 7576 7577 7578 7579
    }

done:
    uirow = MSI_CreateRecord( 3 );
    MSI_RecordSetStringW( uirow, 1, name );
    MSI_RecordSetStringW( uirow, 2, value );
    MSI_RecordSetInteger( uirow, 3, action );
7580
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7581 7582 7583 7584
    msiobj_release( &uirow->hdr );

    if (env) RegCloseKey( env );
    msi_free( deformatted );
7585
    msi_free( new_value );
7586 7587 7588 7589 7590
    return r;
}

static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
{
7591 7592 7593
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7594
    MSIQUERY *view;
7595
    UINT rc;
7596

7597 7598 7599
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveEnvironmentStrings);

7600 7601 7602 7603 7604 7605 7606 7607 7608
    rc = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
    msiobj_release( &view->hdr );
    return rc;
}

7609
UINT msi_validate_product_id( MSIPACKAGE *package )
7610 7611 7612 7613
{
    LPWSTR key, template, id;
    UINT r = ERROR_SUCCESS;

7614
    id = msi_dup_property( package->db, szProductID );
7615 7616 7617 7618 7619
    if (id)
    {
        msi_free( id );
        return ERROR_SUCCESS;
    }
7620 7621
    template = msi_dup_property( package->db, szPIDTemplate );
    key = msi_dup_property( package->db, szPIDKEY );
7622 7623 7624
    if (key && template)
    {
        FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7625
        r = msi_set_property( package->db, szProductID, key, -1 );
7626 7627 7628 7629 7630 7631
    }
    msi_free( template );
    msi_free( key );
    return r;
}

7632 7633 7634 7635 7636
static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
{
    return msi_validate_product_id( package );
}

7637 7638 7639
static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
{
    TRACE("\n");
7640
    package->need_reboot_at_end = 1;
7641 7642 7643
    return ERROR_SUCCESS;
}

7644 7645
static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
{
Hans Leidekker's avatar
Hans Leidekker committed
7646 7647
    static const WCHAR szAvailableFreeReg[] =
        {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7648
    MSIRECORD *uirow;
7649
    int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7650 7651 7652 7653 7654

    TRACE("%p %d kilobytes\n", package, space);

    uirow = MSI_CreateRecord( 1 );
    MSI_RecordSetInteger( uirow, 1, space );
7655
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7656
    msiobj_release( &uirow->hdr );
Hans Leidekker's avatar
Hans Leidekker committed
7657

7658 7659 7660 7661 7662
    return ERROR_SUCCESS;
}

static UINT ACTION_DisableRollback( MSIPACKAGE *package )
{
7663 7664
    TRACE("%p\n", package);

7665
    msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7666 7667 7668 7669 7670 7671 7672 7673 7674
    return ERROR_SUCCESS;
}

static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
{
    FIXME("%p\n", package);
    return ERROR_SUCCESS;
}

7675 7676 7677 7678
static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
{
    static const WCHAR driver_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7679
        'O','D','B','C','D','r','i','v','e','r',0};
7680 7681
    static const WCHAR translator_query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7682 7683 7684
        'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
    MSIQUERY *view;
    UINT r, count;
7685 7686 7687 7688 7689 7690 7691

    r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
    if (r == ERROR_SUCCESS)
    {
        count = 0;
        r = MSI_IterateRecords( view, &count, NULL, package );
        msiobj_release( &view->hdr );
7692 7693
        if (r != ERROR_SUCCESS)
            return r;
7694 7695 7696 7697 7698 7699 7700 7701
        if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
    }
    r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
    if (r == ERROR_SUCCESS)
    {
        count = 0;
        r = MSI_IterateRecords( view, &count, NULL, package );
        msiobj_release( &view->hdr );
7702 7703
        if (r != ERROR_SUCCESS)
            return r;
7704 7705 7706 7707 7708
        if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
    }
    return ERROR_SUCCESS;
}

7709 7710
static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
{
7711
    static const WCHAR fmtW[] =
7712 7713
        {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
         'R','E','M','O','V','E','=','%','s',0};
7714
    MSIPACKAGE *package = param;
7715
    const WCHAR *property = MSI_RecordGetString( rec, 7 );
7716
    int attrs = MSI_RecordGetInteger( rec, 5 );
7717
    UINT len = ARRAY_SIZE( fmtW );
7718 7719 7720 7721
    WCHAR *product, *features, *cmd;
    STARTUPINFOW si;
    PROCESS_INFORMATION info;
    BOOL ret;
7722

7723
    if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7724 7725 7726 7727 7728 7729 7730 7731
    if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;

    deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );

    len += strlenW( product );
    if (features)
        len += strlenW( features );
    else
7732
        len += ARRAY_SIZE( szAll );
7733 7734

    if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7735
    {
7736 7737 7738
        msi_free( product );
        msi_free( features );
        return ERROR_OUTOFMEMORY;
7739
    }
7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751
    sprintfW( cmd, fmtW, product, features ? features : szAll );
    msi_free( product );
    msi_free( features );

    memset( &si, 0, sizeof(STARTUPINFOW) );
    ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
    msi_free( cmd );
    if (!ret) return GetLastError();
    CloseHandle( info.hThread );

    WaitForSingleObject( info.hProcess, INFINITE );
    CloseHandle( info.hProcess );
7752 7753 7754 7755 7756
    return ERROR_SUCCESS;
}

static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
{
7757
    static const WCHAR query[] = {
7758
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7759
    MSIQUERY *view;
7760
    UINT r;
7761 7762 7763 7764 7765 7766

    r = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (r == ERROR_SUCCESS)
    {
        r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
        msiobj_release( &view->hdr );
7767 7768
        if (r != ERROR_SUCCESS)
            return r;
7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809
    }
    return ERROR_SUCCESS;
}

static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
{
    MSIPACKAGE *package = param;
    int attributes = MSI_RecordGetInteger( rec, 5 );

    if (attributes & msidbUpgradeAttributesMigrateFeatures)
    {
        const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
        const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
        const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
        const WCHAR *language = MSI_RecordGetString( rec, 4 );
        HKEY hkey;
        UINT r;

        if (package->Context == MSIINSTALLCONTEXT_MACHINE)
        {
            r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
            if (r != ERROR_SUCCESS)
                return ERROR_SUCCESS;
        }
        else
        {
            r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
            if (r != ERROR_SUCCESS)
                return ERROR_SUCCESS;
        }
        RegCloseKey( hkey );

        FIXME("migrate feature states from %s version min %s version max %s language %s\n",
              debugstr_w(upgrade_code), debugstr_w(version_min),
              debugstr_w(version_max), debugstr_w(language));
    }
    return ERROR_SUCCESS;
}

static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
{
7810 7811 7812
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        'U','p','g','r','a','d','e',0};
7813
    MSIQUERY *view;
7814
    UINT r;
7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830

    if (msi_get_property_int( package->db, szInstalled, 0 ))
    {
        TRACE("product is installed, skipping action\n");
        return ERROR_SUCCESS;
    }
    if (msi_get_property_int( package->db, szPreselected, 0 ))
    {
        TRACE("Preselected property is set, not migrating feature states\n");
        return ERROR_SUCCESS;
    }
    r = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (r == ERROR_SUCCESS)
    {
        r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
        msiobj_release( &view->hdr );
7831 7832
        if (r != ERROR_SUCCESS)
            return r;
7833 7834 7835 7836
    }
    return ERROR_SUCCESS;
}

7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882
static void bind_image( const char *filename, const char *path )
{
    if (!BindImageEx( 0, filename, path, NULL, NULL ))
    {
        WARN("failed to bind image %u\n", GetLastError());
    }
}

static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
{
    UINT i;
    MSIFILE *file;
    MSIPACKAGE *package = param;
    const WCHAR *key = MSI_RecordGetString( rec, 1 );
    const WCHAR *paths = MSI_RecordGetString( rec, 2 );
    char *filenameA, *pathA;
    WCHAR *pathW, **path_list;

    if (!(file = msi_get_loaded_file( package, key )))
    {
        WARN("file %s not found\n", debugstr_w(key));
        return ERROR_SUCCESS;
    }
    if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
    path_list = msi_split_string( paths, ';' );
    if (!path_list) bind_image( filenameA, NULL );
    else
    {
        for (i = 0; path_list[i] && path_list[i][0]; i++)
        {
            deformat_string( package, path_list[i], &pathW );
            if ((pathA = strdupWtoA( pathW )))
            {
                bind_image( filenameA, pathA );
                msi_free( pathA );
            }
            msi_free( pathW );
        }
    }
    msi_free( path_list );
    msi_free( filenameA );
    return ERROR_SUCCESS;
}

static UINT ACTION_BindImage( MSIPACKAGE *package )
{
7883 7884 7885
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
        'B','i','n','d','I','m','a','g','e',0};
7886
    MSIQUERY *view;
7887
    UINT r;
7888 7889 7890 7891 7892 7893

    r = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (r == ERROR_SUCCESS)
    {
        r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
        msiobj_release( &view->hdr );
7894 7895
        if (r != ERROR_SUCCESS)
            return r;
7896 7897 7898 7899
    }
    return ERROR_SUCCESS;
}

7900
static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7901
{
7902
    static const WCHAR query[] = {
7903 7904
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
    MSIQUERY *view;
7905
    DWORD count = 0;
7906
    UINT r;
7907
    
7908 7909
    r = MSI_OpenQuery( package->db, &view, query, table );
    if (r == ERROR_SUCCESS)
7910
    {
7911
        r = MSI_IterateRecords(view, &count, NULL, package);
7912
        msiobj_release(&view->hdr);
7913 7914
        if (r != ERROR_SUCCESS)
            return r;
7915
    }
7916
    if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7917 7918
    return ERROR_SUCCESS;
}
7919

7920
static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7921
{
7922
    static const WCHAR table[] = {
7923
        'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7924 7925
    return msi_unimplemented_action_stub( package, "IsolateComponents", table );
}
7926

7927 7928 7929 7930 7931 7932
static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
{
    static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
    return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
}

7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944
static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
{
    static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
    return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
}

static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
{
    static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
    return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
}

7945 7946 7947 7948 7949 7950
static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
{
    static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
    return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
}

7951 7952 7953
static const struct
{
    const WCHAR *action;
7954 7955
    const UINT description;
    const UINT template;
7956
    UINT (*handler)(MSIPACKAGE *);
7957
    const WCHAR *action_rollback;
7958 7959 7960
}
StandardActions[] =
{
7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036
    { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
    { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
    { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
    { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
    { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
    { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
    { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
    { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
    { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
    { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
    { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
    { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
    { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
    { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
    { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
    { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
    { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
    { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
    { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
    { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
    { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
    { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
    { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
    { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
    { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
    { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
    { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
    { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
    { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
    { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
    { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
    { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
    { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
    { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
    { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
    { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
    { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
    { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
    { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
    { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
    { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
    { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
    { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
    { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
    { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
    { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
    { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
    { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
    { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
    { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
    { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
    { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
    { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
    { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
    { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
    { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
    { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
    { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
    { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
    { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
    { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
    { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
    { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
    { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
    { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
    { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
    { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
    { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
    { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
    { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
    { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
    { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
    { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
    { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
    { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
    { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
8037
    { szINSTALL, 0, 0, ACTION_INSTALL, NULL },
8038
    { 0 }
8039
};
8040

8041
static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
8042
{
8043
    UINT rc = ERROR_FUNCTION_NOT_CALLED;
8044
    void *cookie;
8045
    UINT i;
8046

8047 8048 8049
    if (is_wow64 && package->platform == PLATFORM_X64)
        Wow64DisableWow64FsRedirection(&cookie);

8050 8051 8052
    i = 0;
    while (StandardActions[i].action != NULL)
    {
8053
        if (!strcmpW( StandardActions[i].action, action ))
8054
        {
8055 8056 8057 8058 8059 8060 8061 8062
            WCHAR description[100] = {0}, template[100] = {0};

            if (StandardActions[i].description != 0)
                LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
            if (StandardActions[i].template != 0)
                LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);

            ui_actionstart(package, action, description, template);
8063
            if (StandardActions[i].handler)
8064
            {
8065
                ui_actioninfo( package, action, TRUE, 0 );
8066
                rc = StandardActions[i].handler( package );
8067
                ui_actioninfo( package, action, FALSE, !rc );
8068

8069
                if (StandardActions[i].action_rollback && !package->need_rollback)
8070 8071
                {
                    TRACE("scheduling rollback action\n");
8072
                    msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
8073
                }
8074 8075 8076
            }
            else
            {
8077
                FIXME("unhandled standard action %s\n", debugstr_w(action));
8078
                rc = ERROR_SUCCESS;
8079 8080 8081 8082 8083
            }
            break;
        }
        i++;
    }
8084 8085 8086 8087

    if (is_wow64 && package->platform == PLATFORM_X64)
        Wow64RevertWow64FsRedirection(cookie);

8088
    return rc;
8089
}
8090

8091
UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
8092
{
8093
    UINT rc;
8094 8095 8096

    TRACE("Performing action (%s)\n", debugstr_w(action));

8097
    package->action_progress_increment = 0;
8098
    rc = ACTION_HandleStandardAction(package, action);
8099

8100
    if (rc == ERROR_FUNCTION_NOT_CALLED)
8101
        rc = ACTION_HandleCustomAction(package, action);
8102

8103
    if (rc == ERROR_FUNCTION_NOT_CALLED)
8104 8105 8106 8107 8108
        WARN("unhandled msi action %s\n", debugstr_w(action));

    return rc;
}

8109
static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
8110 8111
{
    UINT rc = ERROR_SUCCESS;
8112
    MSIRECORD *row;
8113

8114
    static const WCHAR query[] =
8115 8116 8117 8118
        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
         '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
         'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
         '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
8119
    static const WCHAR ui_query[] =
8120 8121 8122 8123 8124
        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
     '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
     '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
	 ' ', '=',' ','%','i',0};

8125
    if (needs_ui_sequence(package))
8126
        row = MSI_QueryGetRecord(package->db, ui_query, seq);
8127
    else
8128
        row = MSI_QueryGetRecord(package->db, query, seq);
8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140

    if (row)
    {
        LPCWSTR action, cond;

        TRACE("Running the actions\n");

        /* check conditions */
        cond = MSI_RecordGetString(row, 2);

        /* this is a hack to skip errors in the condition code */
        if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
8141 8142 8143 8144
        {
            msiobj_release(&row->hdr);
            return ERROR_SUCCESS;
        }
8145 8146 8147 8148 8149

        action = MSI_RecordGetString(row, 1);
        if (!action)
        {
            ERR("failed to fetch action\n");
8150 8151
            msiobj_release(&row->hdr);
            return ERROR_FUNCTION_FAILED;
8152 8153
        }

8154
        rc = ACTION_PerformAction(package, action);
8155

8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168
        msiobj_release(&row->hdr);
    }

    return rc;
}

/****************************************************
 * TOP level entry points
 *****************************************************/

UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
                         LPCWSTR szCommandLine )
{
8169
    static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
8170
    static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
8171
    WCHAR *reinstall = NULL, *productcode, *action;
8172
    UINT rc;
8173
    DWORD len = 0;
8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210

    if (szPackagePath)
    {
        LPWSTR p, dir;
        LPCWSTR file;

        dir = strdupW(szPackagePath);
        p = strrchrW(dir, '\\');
        if (p)
        {
            *(++p) = 0;
            file = szPackagePath + (p - dir);
        }
        else
        {
            msi_free(dir);
            dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
            GetCurrentDirectoryW(MAX_PATH, dir);
            lstrcatW(dir, szBackSlash);
            file = szPackagePath;
        }

        msi_free( package->PackagePath );
        package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
        if (!package->PackagePath)
        {
            msi_free(dir);
            return ERROR_OUTOFMEMORY;
        }

        lstrcpyW(package->PackagePath, dir);
        lstrcatW(package->PackagePath, file);
        msi_free(dir);

        msi_set_sourcedir_props(package, FALSE);
    }

8211 8212 8213
    rc = msi_parse_command_line( package, szCommandLine, FALSE );
    if (rc != ERROR_SUCCESS)
        return rc;
8214 8215 8216 8217

    msi_apply_transforms( package );
    msi_apply_patches( package );

8218 8219 8220 8221
    if (msi_get_property( package->db, szAction, NULL, &len ))
        msi_set_property( package->db, szAction, szINSTALL, -1 );
    action = msi_dup_property( package->db, szAction );
    CharUpperW(action);
8222

8223
    msi_set_original_database_property( package->db, szPackagePath );
8224
    msi_parse_command_line( package, szCommandLine, FALSE );
8225
    msi_adjust_privilege_properties( package );
8226 8227
    msi_set_context( package );

8228 8229 8230 8231 8232 8233 8234 8235 8236
    productcode = msi_dup_property( package->db, szProductCode );
    if (strcmpiW( productcode, package->ProductCode ))
    {
        TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
        msi_free( package->ProductCode );
        package->ProductCode = productcode;
    }
    else msi_free( productcode );

8237 8238 8239
    if (msi_get_property_int( package->db, szDisableRollback, 0 ))
    {
        TRACE("disabling rollback\n");
8240
        msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8241 8242
    }

8243
    rc = ACTION_PerformAction(package, action);
8244 8245 8246

    /* process the ending type action */
    if (rc == ERROR_SUCCESS)
8247
        ACTION_PerformActionSequence(package, -1);
8248
    else if (rc == ERROR_INSTALL_USEREXIT)
8249
        ACTION_PerformActionSequence(package, -2);
8250
    else if (rc == ERROR_INSTALL_SUSPEND)
8251
        ACTION_PerformActionSequence(package, -4);
8252
    else  /* failed */
8253
    {
8254
        ACTION_PerformActionSequence(package, -3);
8255 8256 8257 8258
        if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
        {
            package->need_rollback = TRUE;
        }
8259
    }
8260 8261 8262 8263

    /* finish up running custom actions */
    ACTION_FinishCustomActions(package);

8264
    if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
8265 8266
    {
        WARN("installation failed, running rollback script\n");
8267
        execute_script( package, SCRIPT_ROLLBACK );
8268
    }
8269
    msi_free( reinstall );
8270
    msi_free( action );
8271

8272
    if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8273 8274 8275 8276
        return ERROR_SUCCESS_REBOOT_REQUIRED;

    return rc;
}