msiexec.c 25.1 KB
Newer Older
1 2 3
/*
 * msiexec.exe implementation
 *
4
 * Copyright 2004 Vincent Béron
5
 * Copyright 2005 Mike McCormack
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21
 */

22 23
#define WIN32_LEAN_AND_MEAN

24 25
#include <windows.h>
#include <msi.h>
26
#include <winsvc.h>
27 28 29 30
#include <objbase.h>
#include <stdio.h>

#include "wine/debug.h"
Mike McCormack's avatar
Mike McCormack committed
31
#include "wine/unicode.h"
32 33 34

WINE_DEFAULT_DEBUG_CHANNEL(msiexec);

35 36 37
typedef HRESULT (WINAPI *DLLREGISTERSERVER)(void);
typedef HRESULT (WINAPI *DLLUNREGISTERSERVER)(void);

38 39
DWORD DoService(void);

40 41 42
struct string_list
{
	struct string_list *next;
Mike McCormack's avatar
Mike McCormack committed
43
	WCHAR str[1];
44 45
};

Mike McCormack's avatar
Mike McCormack committed
46
static const WCHAR ActionAdmin[] = {
47
   'A','C','T','I','O','N','=','A','D','M','I','N',0 };
Mike McCormack's avatar
Mike McCormack committed
48
static const WCHAR RemoveAll[] = {
49
   'R','E','M','O','V','E','=','A','L','L',0 };
50

51 52 53 54 55 56 57 58
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','\\',
   'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};

59 60
static void ShowUsage(int ExitCode)
{
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    WCHAR msiexec_version[40];
    WCHAR filename[MAX_PATH];
    LPWSTR msi_res;
    LPWSTR msiexec_help;
    HMODULE hmsi = GetModuleHandleA("msi.dll");
    DWORD len;
    DWORD res;

    /* MsiGetFileVersion need the full path */
    *filename = 0;
    res = GetModuleFileNameW(hmsi, filename, sizeof(filename) / sizeof(filename[0]));
    if (!res)
        WINE_ERR("GetModuleFileName failed: %d\n", GetLastError());

    len = sizeof(msiexec_version) / sizeof(msiexec_version[0]);
    *msiexec_version = 0;
    res = MsiGetFileVersionW(filename, msiexec_version, &len, NULL, NULL);
    if (res)
        WINE_ERR("MsiGetFileVersion failed with %d\n", res);

    /* Return the length of the resource.
       No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
    len = LoadStringW(hmsi, 10, (LPWSTR) &msi_res, 0);

    msi_res = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
    msiexec_help = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) + sizeof(msiexec_version));
    if (msi_res && msiexec_help) {
        *msi_res = 0;
        LoadStringW(hmsi, 10, msi_res, len + 1);

        sprintfW(msiexec_help, msi_res, msiexec_version);
        MsiMessageBoxW(0, msiexec_help, NULL, 0, GetUserDefaultLangID(), 0);
    }
    HeapFree(GetProcessHeap(), 0, msi_res);
    HeapFree(GetProcessHeap(), 0, msiexec_help);
    ExitProcess(ExitCode);
97 98
}

99
static BOOL IsProductCode(LPWSTR str)
100
{
101
	GUID ProductCode;
102

103 104 105
	if(lstrlenW(str) != 38)
		return FALSE;
	return ( (CLSIDFromString(str, &ProductCode) == NOERROR) );
106

107 108
}

Mike McCormack's avatar
Mike McCormack committed
109
static VOID StringListAppend(struct string_list **list, LPCWSTR str)
110
{
111 112
	struct string_list *entry;

113
	entry = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(struct string_list, str[lstrlenW(str) + 1]));
114
	if(!entry)
115 116 117 118
	{
		WINE_ERR("Out of memory!\n");
		ExitProcess(1);
	}
Mike McCormack's avatar
Mike McCormack committed
119
	lstrcpyW(entry->str, str);
120 121 122 123 124 125 126 127 128 129 130
	entry->next = NULL;

	/*
	 * Ignoring o(n^2) time complexity to add n strings for simplicity,
	 *  add the string to the end of the list to preserve the order.
	 */
	while( *list )
		list = &(*list)->next;
	*list = entry;
}

Mike McCormack's avatar
Mike McCormack committed
131
static LPWSTR build_properties(struct string_list *property_list)
132 133
{
	struct string_list *list;
Mike McCormack's avatar
Mike McCormack committed
134
	LPWSTR ret, p, value;
135 136 137 138 139 140 141 142 143
	DWORD len;
	BOOL needs_quote;

	if(!property_list)
		return NULL;

	/* count the space we need */
	len = 1;
	for(list = property_list; list; list = list->next)
Mike McCormack's avatar
Mike McCormack committed
144
		len += lstrlenW(list->str) + 3;
145

Mike McCormack's avatar
Mike McCormack committed
146
	ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
147 148 149 150 151

	/* add a space before each string, and quote the value */
	p = ret;
	for(list = property_list; list; list = list->next)
	{
Mike McCormack's avatar
Mike McCormack committed
152
		value = strchrW(list->str,'=');
153 154 155 156
		if(!value)
			continue;
		len = value - list->str;
		*p++ = ' ';
157
		memcpy(p, list->str, len * sizeof(WCHAR));
158 159 160 161 162
		p += len;
		*p++ = '=';

		/* check if the value contains spaces and maybe quote it */
		value++;
Mike McCormack's avatar
Mike McCormack committed
163
		needs_quote = strchrW(value,' ') ? 1 : 0;
164 165
		if(needs_quote)
			*p++ = '"';
Mike McCormack's avatar
Mike McCormack committed
166
		len = lstrlenW(value);
167
		memcpy(p, value, len * sizeof(WCHAR));
168 169 170 171 172 173
		p += len;
		if(needs_quote)
			*p++ = '"';
	}
	*p = 0;

Mike McCormack's avatar
Mike McCormack committed
174 175
	WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );

176
	return ret;
177 178
}

Mike McCormack's avatar
Mike McCormack committed
179
static LPWSTR build_transforms(struct string_list *transform_list)
180
{
181
	struct string_list *list;
Mike McCormack's avatar
Mike McCormack committed
182
	LPWSTR ret, p;
183 184 185 186 187
	DWORD len;

	/* count the space we need */
	len = 1;
	for(list = transform_list; list; list = list->next)
Mike McCormack's avatar
Mike McCormack committed
188
		len += lstrlenW(list->str) + 1;
189

Mike McCormack's avatar
Mike McCormack committed
190
	ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
191 192 193 194 195

	/* add all the transforms with a semicolon between each one */
	p = ret;
	for(list = transform_list; list; list = list->next)
	{
Mike McCormack's avatar
Mike McCormack committed
196 197
		len = lstrlenW(list->str);
		lstrcpynW(p, list->str, len );
198 199 200 201 202 203 204
		p += len;
		if(list->next)
			*p++ = ';';
	}
	*p = 0;

	return ret;
205 206
}

Mike McCormack's avatar
Mike McCormack committed
207
static DWORD msi_atou(LPCWSTR str)
208
{
Mike McCormack's avatar
Mike McCormack committed
209 210 211 212 213 214 215
	DWORD ret = 0;
	while(*str >= '0' && *str <= '9')
	{
		ret *= 10;
		ret += (*str - '0');
		str++;
	}
216
	return ret;
Mike McCormack's avatar
Mike McCormack committed
217
}
218

219 220 221 222 223 224 225 226
static LPWSTR msi_strdup(LPCWSTR str)
{
	DWORD len = lstrlenW(str)+1;
	LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
	lstrcpyW(ret, str);
	return ret;
}

Mike McCormack's avatar
Mike McCormack committed
227 228 229 230 231 232 233 234
/* str1 is the same as str2, ignoring case */
static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
{
	DWORD len, ret;
	LPWSTR strW;

	len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
	if( !len )
235
		return FALSE;
Mike McCormack's avatar
Mike McCormack committed
236
	if( lstrlenW(str1) != (len-1) )
237
		return FALSE;
Mike McCormack's avatar
Mike McCormack committed
238 239 240 241
	strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
	MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
	ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len);
	HeapFree(GetProcessHeap(), 0, strW);
242
	return (ret == CSTR_EQUAL);
Mike McCormack's avatar
Mike McCormack committed
243
}
244

245 246 247 248
/* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
static BOOL msi_option_equal(LPCWSTR str1, LPCSTR str2)
{
    if (str1[0] != '/' && str1[0] != '-')
249
        return FALSE;
250 251 252 253 254

    /* skip over the hyphen or slash */
    return msi_strequal(str1 + 1, str2);
}

Mike McCormack's avatar
Mike McCormack committed
255 256 257 258 259 260 261 262
/* str2 is at the beginning of str1, ignoring case */
static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2)
{
	DWORD len, ret;
	LPWSTR strW;

	len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
	if( !len )
263
		return FALSE;
Mike McCormack's avatar
Mike McCormack committed
264
	if( lstrlenW(str1) < (len-1) )
265
		return FALSE;
Mike McCormack's avatar
Mike McCormack committed
266 267 268 269
	strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
	MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
	ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1);
	HeapFree(GetProcessHeap(), 0, strW);
270
	return (ret == CSTR_EQUAL);
271 272
}

273 274 275 276
/* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
static BOOL msi_option_prefix(LPCWSTR str1, LPCSTR str2)
{
    if (str1[0] != '/' && str1[0] != '-')
277
        return FALSE;
278 279 280 281 282

    /* skip over the hyphen or slash */
    return msi_strprefix(str1 + 1, str2);
}

Mike McCormack's avatar
Mike McCormack committed
283
static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
284 285 286
{
	VOID* (*proc)(void);

Mike McCormack's avatar
Mike McCormack committed
287
	*DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
288 289
	if(!*DllHandle)
	{
Mike McCormack's avatar
Mike McCormack committed
290
		fprintf(stderr, "Unable to load dll %s\n", wine_dbgstr_w(DllName));
291 292 293 294 295
		ExitProcess(1);
	}
	proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
	if(!proc)
	{
Mike McCormack's avatar
Mike McCormack committed
296 297
		fprintf(stderr, "Dll %s does not implement function %s\n",
			wine_dbgstr_w(DllName), ProcName);
298 299 300 301 302 303 304
		FreeLibrary(*DllHandle);
		ExitProcess(1);
	}

	return proc;
}

305
static DWORD DoDllRegisterServer(LPCWSTR DllName)
306 307 308 309 310 311 312 313 314 315
{
	HRESULT hr;
	DLLREGISTERSERVER pfDllRegisterServer = NULL;
	HMODULE DllHandle = NULL;

	pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);

	hr = pfDllRegisterServer();
	if(FAILED(hr))
	{
Mike McCormack's avatar
Mike McCormack committed
316
		fprintf(stderr, "Failed to register dll %s\n", wine_dbgstr_w(DllName));
317
		return 1;
318
	}
Mike McCormack's avatar
Mike McCormack committed
319
	printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
320 321
	if(DllHandle)
		FreeLibrary(DllHandle);
322
	return 0;
323 324
}

325
static DWORD DoDllUnregisterServer(LPCWSTR DllName)
326 327 328 329 330 331 332 333 334 335
{
	HRESULT hr;
	DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
	HMODULE DllHandle = NULL;

	pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);

	hr = pfDllUnregisterServer();
	if(FAILED(hr))
	{
Mike McCormack's avatar
Mike McCormack committed
336
		fprintf(stderr, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
337
		return 1;
338
	}
Mike McCormack's avatar
Mike McCormack committed
339
	printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
340 341
	if(DllHandle)
		FreeLibrary(DllHandle);
342
	return 0;
343 344
}

345 346
static DWORD DoRegServer(void)
{
347 348
    static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
    static const WCHAR msiexecW[] = {'\\','m','s','i','e','x','e','c',' ','/','V',0};
349
    SC_HANDLE scm, service;
350 351
    WCHAR path[MAX_PATH+12];
    DWORD len, ret = 0;
352

353
    if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CREATE_SERVICE)))
354 355 356 357
    {
        fprintf(stderr, "Failed to open the service control manager.\n");
        return 1;
    }
358 359 360 361 362 363 364 365
    len = GetSystemDirectoryW(path, MAX_PATH);
    lstrcpyW(path + len, msiexecW);
    if ((service = CreateServiceW(scm, msiserverW, msiserverW, GENERIC_ALL,
                                  SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START,
                                  SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, NULL, NULL)))
    {
        CloseServiceHandle(service);
    }
366
    else if (GetLastError() != ERROR_SERVICE_EXISTS)
367 368
    {
        fprintf(stderr, "Failed to create MSI service\n");
369
        ret = 1;
370
    }
371 372
    CloseServiceHandle(scm);
    return ret;
373 374
}

375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
static DWORD DoUnregServer(void)
{
    static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
    SC_HANDLE scm, service;
    DWORD ret = 0;

    if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CONNECT)))
    {
        fprintf(stderr, "Failed to open service control manager\n");
        return 1;
    }
    if ((service = OpenServiceW(scm, msiserverW, DELETE)))
    {
        if (!DeleteService(service))
        {
            fprintf(stderr, "Failed to delete MSI service\n");
            ret = 1;
        }
        CloseServiceHandle(service);
    }
    else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
    {
        fprintf(stderr, "Failed to open MSI service\n");
        ret = 1;
    }
    CloseServiceHandle(scm);
    return ret;
}

404 405 406 407 408 409
static INT DoEmbedding( LPWSTR key )
{
	printf("Remote custom actions are not supported yet\n");
	return 1;
}

410 411 412 413 414 415 416 417 418 419 420 421 422
/*
 * state machine to break up the command line properly
 */

enum chomp_state
{
	cs_whitespace,
	cs_token,
	cs_quote
};

static int chomp( WCHAR *str )
{
423
	enum chomp_state state = cs_token;
424
	WCHAR *p, *out;
425 426
        int count = 1;
        BOOL ignore;
427 428 429

	for( p = str, out = str; *p; p++ )
	{
430
                ignore = TRUE;
431 432 433 434 435 436 437 438 439 440 441 442 443
		switch( state )
		{
		case cs_whitespace:
			switch( *p )
			{
			case ' ':
				break;
			case '"':
				state = cs_quote;
				count++;
				break;
			default:
				count++;
444
                                ignore = FALSE;
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
				state = cs_token;
			}
			break;

		case cs_token:
			switch( *p )
			{
			case '"':
				state = cs_quote;
				break;
			case ' ':
				state = cs_whitespace;
				*out++ = 0;
				break;
			default:
460
                                ignore = FALSE;
461 462 463 464 465 466 467 468 469 470
			}
			break;

		case cs_quote:
			switch( *p )
			{
			case '"':
				state = cs_token;
				break;
			default:
471
                                ignore = FALSE;
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
			}
			break;
		}
		if( !ignore )
			*out++ = *p;
	}

	*out = 0;

	return count;
}

static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
{
	WCHAR **argv, *p = msi_strdup(cmdline);
	int i, n;

	n = chomp( p );
	argv = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR*)*(n+1));
	for( i=0; i<n; i++ )
	{
		argv[i] = p;
		p += lstrlenW(p) + 1;
	}
	argv[i] = NULL;

	*pargc = n;
	*pargv = argv;
}

502
static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***pargv )
503 504
{
	LONG r;
505
	HKEY hkey;
506
	DWORD sz = 0, type = 0;
507
	WCHAR *buf;
508 509 510 511 512 513 514 515
	BOOL ret = FALSE;

	r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
	if(r != ERROR_SUCCESS)
		return FALSE;
	r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
	if(r == ERROR_SUCCESS && type == REG_SZ)
	{
516 517 518 519 520 521 522 523 524
		int len = lstrlenW( *pargv[0] );
		if (!(buf = HeapAlloc( GetProcessHeap(), 0, sz + (len + 1) * sizeof(WCHAR) )))
		{
			RegCloseKey( hkey );
			return FALSE;
		}
		memcpy( buf, *pargv[0], len * sizeof(WCHAR) );
		buf[len++] = ' ';
		r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)(buf + len), &sz);
525 526
		if( r == ERROR_SUCCESS )
		{
527
			process_args(buf, pargc, pargv);
528 529
			ret = TRUE;
		}
530
		HeapFree(GetProcessHeap(), 0, buf);
531
	}
532
	RegCloseKey(hkey);
533 534 535
	return ret;
}

536
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
537 538 539
{
	int i;
	BOOL FunctionInstall = FALSE;
540
	BOOL FunctionInstallAdmin = FALSE;
541
	BOOL FunctionRepair = FALSE;
542
	BOOL FunctionAdvertise = FALSE;
543
	BOOL FunctionPatch = FALSE;
544 545
	BOOL FunctionDllRegisterServer = FALSE;
	BOOL FunctionDllUnregisterServer = FALSE;
546 547
	BOOL FunctionRegServer = FALSE;
	BOOL FunctionUnregServer = FALSE;
548
	BOOL FunctionServer = FALSE;
549
	BOOL FunctionUnknown = FALSE;
550

551
	LPWSTR PackageName = NULL;
Mike McCormack's avatar
Mike McCormack committed
552
	LPWSTR Properties = NULL;
553
	struct string_list *property_list = NULL;
554

555 556
	DWORD RepairMode = 0;

557
	DWORD_PTR AdvertiseMode = 0;
558
	struct string_list *transform_list = NULL;
559 560
	LANGID Language = 0;

561
	DWORD LogMode = 0;
Mike McCormack's avatar
Mike McCormack committed
562
	LPWSTR LogFileName = NULL;
563 564
	DWORD LogAttributes = 0;

Mike McCormack's avatar
Mike McCormack committed
565
	LPWSTR PatchFileName = NULL;
566 567
	INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;

568
	INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
569

Mike McCormack's avatar
Mike McCormack committed
570
	LPWSTR DllName = NULL;
571
	DWORD ReturnCode;
572
	int argc;
Mike McCormack's avatar
Mike McCormack committed
573
	LPWSTR *argvW = NULL;
574

575
	/* parse the command line */
576
	process_args( GetCommandLineW(), &argc, &argvW );
577

578 579 580 581 582 583
	/*
	 * If the args begin with /@ IDENT then we need to load the real
	 * command line out of the RunOnceEntries key in the registry.
	 *  We do that before starting to process the real commandline,
	 * then overwrite the commandline again.
	 */
584
	if(argc>1 && msi_option_equal(argvW[1], "@"))
585 586 587 588 589
	{
		if(!process_args_from_reg( argvW[2], &argc, &argvW ))
			return 1;
	}

590 591 592
	if (argc == 3 && msi_option_equal(argvW[1], "Embedding"))
		return DoEmbedding( argvW[2] );

593 594
	for(i = 1; i < argc; i++)
	{
Mike McCormack's avatar
Mike McCormack committed
595
		WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
596

597
		if (msi_option_equal(argvW[i], "regserver"))
598 599 600
		{
			FunctionRegServer = TRUE;
		}
601 602
		else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")
			||  msi_option_equal(argvW[i], "unreg"))
603 604 605
		{
			FunctionUnregServer = TRUE;
		}
606
		else if(msi_option_prefix(argvW[i], "i") || msi_option_prefix(argvW[i], "package"))
607
		{
Mike McCormack's avatar
Mike McCormack committed
608
			LPWSTR argvWi = argvW[i];
609
			int argLen = (msi_option_prefix(argvW[i], "i") ? 2 : 8);
610
			FunctionInstall = TRUE;
611 612
			if(lstrlenW(argvW[i]) > argLen)
				argvWi += argLen;
613 614
			else
			{
615 616 617
				i++;
				if(i >= argc)
					ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
618 619
				WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
				argvWi = argvW[i];
620
			}
621
			PackageName = argvWi;
622
		}
623
		else if(msi_option_equal(argvW[i], "a"))
624 625
		{
			FunctionInstall = TRUE;
626 627
			FunctionInstallAdmin = TRUE;
			InstallType = INSTALLTYPE_NETWORK_IMAGE;
628 629 630
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
631 632
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			PackageName = argvW[i];
633
			StringListAppend(&property_list, ActionAdmin);
634
			WINE_FIXME("Administrative installs are not currently supported\n");
635
		}
636
		else if(msi_option_prefix(argvW[i], "f"))
637
		{
638
			int j;
Mike McCormack's avatar
Mike McCormack committed
639
			int len = lstrlenW(argvW[i]);
640 641 642
			FunctionRepair = TRUE;
			for(j = 2; j < len; j++)
			{
Mike McCormack's avatar
Mike McCormack committed
643
				switch(argvW[i][j])
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
				{
					case 'P':
					case 'p':
						RepairMode |= REINSTALLMODE_FILEMISSING;
						break;
					case 'O':
					case 'o':
						RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
						break;
					case 'E':
					case 'e':
						RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
						break;
					case 'D':
					case 'd':
						RepairMode |= REINSTALLMODE_FILEEXACT;
						break;
					case 'C':
					case 'c':
						RepairMode |= REINSTALLMODE_FILEVERIFY;
						break;
					case 'A':
					case 'a':
						RepairMode |= REINSTALLMODE_FILEREPLACE;
						break;
					case 'U':
					case 'u':
						RepairMode |= REINSTALLMODE_USERDATA;
						break;
					case 'M':
					case 'm':
						RepairMode |= REINSTALLMODE_MACHINEDATA;
						break;
					case 'S':
					case 's':
						RepairMode |= REINSTALLMODE_SHORTCUT;
						break;
					case 'V':
					case 'v':
						RepairMode |= REINSTALLMODE_PACKAGE;
						break;
					default:
Mike McCormack's avatar
Mike McCormack committed
686
						fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
687 688 689
						break;
				}
			}
690 691 692 693 694 695 696 697
			if(len == 2)
			{
				RepairMode = REINSTALLMODE_FILEMISSING |
					REINSTALLMODE_FILEEQUALVERSION |
					REINSTALLMODE_FILEVERIFY |
					REINSTALLMODE_MACHINEDATA |
					REINSTALLMODE_SHORTCUT;
			}
698 699 700
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
701
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
702
			PackageName = argvW[i];
703
		}
704
		else if(msi_option_prefix(argvW[i], "x") || msi_option_equal(argvW[i], "uninstall"))
705 706
		{
			FunctionInstall = TRUE;
707 708
			if(msi_option_prefix(argvW[i], "x")) PackageName = argvW[i]+2;
			if(!PackageName || !PackageName[0])
709 710 711 712 713 714 715
			{
				i++;
				if (i >= argc)
					ShowUsage(1);
				PackageName = argvW[i];
			}
			WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName));
716
			StringListAppend(&property_list, RemoveAll);
717
		}
718
		else if(msi_option_prefix(argvW[i], "j"))
719
		{
720
			int j;
Mike McCormack's avatar
Mike McCormack committed
721
			int len = lstrlenW(argvW[i]);
722 723 724
			FunctionAdvertise = TRUE;
			for(j = 2; j < len; j++)
			{
Mike McCormack's avatar
Mike McCormack committed
725
				switch(argvW[i][j])
726 727 728 729 730 731 732 733 734 735
				{
					case 'U':
					case 'u':
						AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
						break;
					case 'M':
					case 'm':
						AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
						break;
					default:
Mike McCormack's avatar
Mike McCormack committed
736
						fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
737 738 739 740 741 742
						break;
				}
			}
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
743 744
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			PackageName = argvW[i];
745
		}
746
		else if(msi_strequal(argvW[i], "u"))
747 748 749
		{
			FunctionAdvertise = TRUE;
			AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
750 751 752
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
753 754
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			PackageName = argvW[i];
755
		}
756
		else if(msi_strequal(argvW[i], "m"))
757
		{
758 759
			FunctionAdvertise = TRUE;
			AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
760 761 762
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
763 764
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			PackageName = argvW[i];
765
		}
766
		else if(msi_option_equal(argvW[i], "t"))
767 768 769 770
		{
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
771 772
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			StringListAppend(&transform_list, argvW[i]);
773
		}
774
		else if(msi_option_equal(argvW[i], "g"))
775 776 777 778
		{
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
779 780
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			Language = msi_atou(argvW[i]);
781
		}
782
		else if(msi_option_prefix(argvW[i], "l"))
783
		{
784
			int j;
Mike McCormack's avatar
Mike McCormack committed
785
			int len = lstrlenW(argvW[i]);
786 787
			for(j = 2; j < len; j++)
			{
Mike McCormack's avatar
Mike McCormack committed
788
				switch(argvW[i][j])
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
				{
					case 'I':
					case 'i':
						LogMode |= INSTALLLOGMODE_INFO;
						break;
					case 'W':
					case 'w':
						LogMode |= INSTALLLOGMODE_WARNING;
						break;
					case 'E':
					case 'e':
						LogMode |= INSTALLLOGMODE_ERROR;
						break;
					case 'A':
					case 'a':
						LogMode |= INSTALLLOGMODE_ACTIONSTART;
						break;
					case 'R':
					case 'r':
						LogMode |= INSTALLLOGMODE_ACTIONDATA;
						break;
					case 'U':
					case 'u':
						LogMode |= INSTALLLOGMODE_USER;
						break;
					case 'C':
					case 'c':
						LogMode |= INSTALLLOGMODE_COMMONDATA;
						break;
					case 'M':
					case 'm':
						LogMode |= INSTALLLOGMODE_FATALEXIT;
						break;
					case 'O':
					case 'o':
						LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
						break;
					case 'P':
					case 'p':
						LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
						break;
					case 'V':
					case 'v':
						LogMode |= INSTALLLOGMODE_VERBOSE;
						break;
					case '*':
						LogMode = INSTALLLOGMODE_FATALEXIT |
							INSTALLLOGMODE_ERROR |
							INSTALLLOGMODE_WARNING |
							INSTALLLOGMODE_USER |
							INSTALLLOGMODE_INFO |
							INSTALLLOGMODE_RESOLVESOURCE |
							INSTALLLOGMODE_OUTOFDISKSPACE |
							INSTALLLOGMODE_ACTIONSTART |
							INSTALLLOGMODE_ACTIONDATA |
							INSTALLLOGMODE_COMMONDATA |
							INSTALLLOGMODE_PROPERTYDUMP |
							INSTALLLOGMODE_PROGRESS |
							INSTALLLOGMODE_INITIALIZE |
							INSTALLLOGMODE_TERMINATE |
							INSTALLLOGMODE_SHOWDIALOG;
						break;
					case '+':
						LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
						break;
					case '!':
						LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
						break;
					default:
						break;
				}
			}
861 862 863
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
864 865 866
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			LogFileName = argvW[i];
			if(MsiEnableLogW(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
867
			{
868
				fprintf(stderr, "Logging in %s (0x%08x, %u) failed\n",
Mike McCormack's avatar
Mike McCormack committed
869
					 wine_dbgstr_w(LogFileName), LogMode, LogAttributes);
870 871
				ExitProcess(1);
			}
872
		}
873
		else if(msi_option_equal(argvW[i], "p") || msi_option_equal(argvW[i], "update"))
874
		{
875
			FunctionPatch = TRUE;
876 877 878
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
879 880
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			PatchFileName = argvW[i];
881
		}
882
		else if(msi_option_prefix(argvW[i], "q"))
883
		{
884 885
			if(lstrlenW(argvW[i]) == 2 || msi_strequal(argvW[i]+2, "n") ||
			   msi_strequal(argvW[i] + 2, "uiet"))
886 887 888
			{
				InstallUILevel = INSTALLUILEVEL_NONE;
			}
889
			else if(msi_strequal(argvW[i]+2, "b"))
890 891 892
			{
				InstallUILevel = INSTALLUILEVEL_BASIC;
			}
893
			else if(msi_strequal(argvW[i]+2, "r"))
894 895 896
			{
				InstallUILevel = INSTALLUILEVEL_REDUCED;
			}
897
			else if(msi_strequal(argvW[i]+2, "f"))
898 899 900
			{
				InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
			}
901
			else if(msi_strequal(argvW[i]+2, "n+"))
902 903 904
			{
				InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
			}
905
			else if(msi_strequal(argvW[i]+2, "b+"))
906 907 908
			{
				InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
			}
909
			else if(msi_strequal(argvW[i]+2, "b-"))
910 911 912
			{
				InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
			}
913
			else if(msi_strequal(argvW[i]+2, "b+!"))
914
			{
915 916 917 918 919 920 921
				WINE_FIXME("Unhandled modifier: !\n");
				InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG|INSTALLUILEVEL_HIDECANCEL;
			}
			else if(msi_strequal(argvW[i]+2, "b-!"))
			{
				WINE_FIXME("Unhandled modifier: !\n");
				InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY|INSTALLUILEVEL_HIDECANCEL;
922
			}
923 924
			else if(msi_strequal(argvW[i]+2, "b!"))
			{
925 926
				WINE_FIXME("Unhandled modifier: !\n");
				InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_HIDECANCEL;
927
			}
928 929
			else
			{
Mike McCormack's avatar
Mike McCormack committed
930 931
				fprintf(stderr, "Unknown option \"%s\" for UI level\n",
					 wine_dbgstr_w(argvW[i]+2));
932
			}
933
		}
934
		else if(msi_option_equal(argvW[i], "y"))
935 936 937 938 939
		{
			FunctionDllRegisterServer = TRUE;
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
940 941
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			DllName = argvW[i];
942
		}
943
		else if(msi_option_equal(argvW[i], "z"))
944 945 946 947 948
		{
			FunctionDllUnregisterServer = TRUE;
			i++;
			if(i >= argc)
				ShowUsage(1);
Mike McCormack's avatar
Mike McCormack committed
949 950
			WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
			DllName = argvW[i];
951
		}
952
		else if(msi_option_equal(argvW[i], "help") || msi_option_equal(argvW[i], "?"))
953 954 955
		{
			ShowUsage(0);
		}
956
		else if(msi_option_equal(argvW[i], "m"))
957 958 959 960
		{
			FunctionUnknown = TRUE;
			WINE_FIXME("Unknown parameter /m\n");
		}
961
		else if(msi_option_equal(argvW[i], "D"))
962 963 964 965
		{
			FunctionUnknown = TRUE;
			WINE_FIXME("Unknown parameter /D\n");
		}
966 967 968 969
		else if (msi_option_equal(argvW[i], "V"))
		{
		    FunctionServer = TRUE;
		}
970
		else
971
			StringListAppend(&property_list, argvW[i]);
972 973
	}

974 975 976
	/* start the GUI */
	MsiSetInternalUI(InstallUILevel, NULL);

977
	Properties = build_properties( property_list );
978

979 980 981
	if(FunctionInstallAdmin && FunctionPatch)
		FunctionInstall = FALSE;

982
	ReturnCode = 1;
983 984
	if(FunctionInstall)
	{
985 986
		if(IsProductCode(PackageName))
			ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
987
		else
Mike McCormack's avatar
Mike McCormack committed
988
			ReturnCode = MsiInstallProductW(PackageName, Properties);
989
	}
990 991
	else if(FunctionRepair)
	{
992
		if(IsProductCode(PackageName))
993 994
			WINE_FIXME("Product code treatment not implemented yet\n");
		else
Mike McCormack's avatar
Mike McCormack committed
995
			ReturnCode = MsiReinstallProductW(PackageName, RepairMode);
996
	}
997 998
	else if(FunctionAdvertise)
	{
999
		LPWSTR Transforms = build_transforms( property_list );
Mike McCormack's avatar
Mike McCormack committed
1000
		ReturnCode = MsiAdvertiseProductW(PackageName, (LPWSTR) AdvertiseMode, Transforms, Language);
1001
	}
1002 1003
	else if(FunctionPatch)
	{
Mike McCormack's avatar
Mike McCormack committed
1004
		ReturnCode = MsiApplyPatchW(PatchFileName, PackageName, InstallType, Properties);
1005
	}
1006 1007
	else if(FunctionDllRegisterServer)
	{
1008
		ReturnCode = DoDllRegisterServer(DllName);
1009 1010 1011
	}
	else if(FunctionDllUnregisterServer)
	{
1012
		ReturnCode = DoDllUnregisterServer(DllName);
1013
	}
1014 1015
	else if (FunctionRegServer)
	{
1016
		ReturnCode = DoRegServer();
1017 1018 1019
	}
	else if (FunctionUnregServer)
	{
1020
		ReturnCode = DoUnregServer();
1021
	}
1022 1023 1024 1025
	else if (FunctionServer)
	{
	    ReturnCode = DoService();
	}
1026 1027 1028 1029
	else if (FunctionUnknown)
	{
		WINE_FIXME( "Unknown function, ignoring\n" );
	}
1030 1031 1032
	else
		ShowUsage(1);

1033
	return ReturnCode;
1034
}