vbscript: Added Hex function tests.
[wine] / programs / msiexec / msiexec.c
1 /*
2  * msiexec.exe implementation
3  *
4  * Copyright 2004 Vincent BĂ©ron
5  * Copyright 2005 Mike McCormack
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * 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 #define WIN32_LEAN_AND_MEAN
23
24 #include <windows.h>
25 #include <msi.h>
26 #include <objbase.h>
27 #include <stdio.h>
28
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
33
34 typedef HRESULT (WINAPI *DLLREGISTERSERVER)(void);
35 typedef HRESULT (WINAPI *DLLUNREGISTERSERVER)(void);
36
37 DWORD DoService(void);
38
39 struct string_list
40 {
41         struct string_list *next;
42         WCHAR str[1];
43 };
44
45 static const WCHAR ActionAdmin[] = {
46    'A','C','T','I','O','N','=','A','D','M','I','N',0 };
47 static const WCHAR RemoveAll[] = {
48    'R','E','M','O','V','E','=','A','L','L',0 };
49
50 static const WCHAR InstallRunOnce[] = {
51    'S','o','f','t','w','a','r','e','\\',
52    'M','i','c','r','o','s','o','f','t','\\',
53    'W','i','n','d','o','w','s','\\',
54    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
55    'I','n','s','t','a','l','l','e','r','\\',
56    'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
57
58 static void ShowUsage(int ExitCode)
59 {
60     WCHAR msiexec_version[40];
61     WCHAR filename[MAX_PATH];
62     LPWSTR msi_res;
63     LPWSTR msiexec_help;
64     HMODULE hmsi = GetModuleHandleA("msi.dll");
65     DWORD len;
66     DWORD res;
67
68     /* MsiGetFileVersion need the full path */
69     *filename = 0;
70     res = GetModuleFileNameW(hmsi, filename, sizeof(filename) / sizeof(filename[0]));
71     if (!res)
72         WINE_ERR("GetModuleFileName failed: %d\n", GetLastError());
73
74     len = sizeof(msiexec_version) / sizeof(msiexec_version[0]);
75     *msiexec_version = 0;
76     res = MsiGetFileVersionW(filename, msiexec_version, &len, NULL, NULL);
77     if (res)
78         WINE_ERR("MsiGetFileVersion failed with %d\n", res);
79
80     /* Return the length of the resource.
81        No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
82     len = LoadStringW(hmsi, 10, (LPWSTR) &msi_res, 0);
83
84     msi_res = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
85     msiexec_help = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) + sizeof(msiexec_version));
86     if (msi_res && msiexec_help) {
87         *msi_res = 0;
88         LoadStringW(hmsi, 10, msi_res, len + 1);
89
90         sprintfW(msiexec_help, msi_res, msiexec_version);
91         MsiMessageBoxW(0, msiexec_help, NULL, 0, GetUserDefaultLangID(), 0);
92     }
93     HeapFree(GetProcessHeap(), 0, msi_res);
94     HeapFree(GetProcessHeap(), 0, msiexec_help);
95     ExitProcess(ExitCode);
96 }
97
98 static BOOL IsProductCode(LPWSTR str)
99 {
100         GUID ProductCode;
101
102         if(lstrlenW(str) != 38)
103                 return FALSE;
104         return ( (CLSIDFromString(str, &ProductCode) == NOERROR) );
105
106 }
107
108 static VOID StringListAppend(struct string_list **list, LPCWSTR str)
109 {
110         struct string_list *entry;
111         DWORD size;
112
113         size = sizeof *entry + lstrlenW(str) * sizeof (WCHAR);
114         entry = HeapAlloc(GetProcessHeap(), 0, size);
115         if(!entry)
116         {
117                 WINE_ERR("Out of memory!\n");
118                 ExitProcess(1);
119         }
120         lstrcpyW(entry->str, str);
121         entry->next = NULL;
122
123         /*
124          * Ignoring o(n^2) time complexity to add n strings for simplicity,
125          *  add the string to the end of the list to preserve the order.
126          */
127         while( *list )
128                 list = &(*list)->next;
129         *list = entry;
130 }
131
132 static LPWSTR build_properties(struct string_list *property_list)
133 {
134         struct string_list *list;
135         LPWSTR ret, p, value;
136         DWORD len;
137         BOOL needs_quote;
138
139         if(!property_list)
140                 return NULL;
141
142         /* count the space we need */
143         len = 1;
144         for(list = property_list; list; list = list->next)
145                 len += lstrlenW(list->str) + 3;
146
147         ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
148
149         /* add a space before each string, and quote the value */
150         p = ret;
151         for(list = property_list; list; list = list->next)
152         {
153                 value = strchrW(list->str,'=');
154                 if(!value)
155                         continue;
156                 len = value - list->str;
157                 *p++ = ' ';
158                 memcpy(p, list->str, len * sizeof(WCHAR));
159                 p += len;
160                 *p++ = '=';
161
162                 /* check if the value contains spaces and maybe quote it */
163                 value++;
164                 needs_quote = strchrW(value,' ') ? 1 : 0;
165                 if(needs_quote)
166                         *p++ = '"';
167                 len = lstrlenW(value);
168                 memcpy(p, value, len * sizeof(WCHAR));
169                 p += len;
170                 if(needs_quote)
171                         *p++ = '"';
172         }
173         *p = 0;
174
175         WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );
176
177         return ret;
178 }
179
180 static LPWSTR build_transforms(struct string_list *transform_list)
181 {
182         struct string_list *list;
183         LPWSTR ret, p;
184         DWORD len;
185
186         /* count the space we need */
187         len = 1;
188         for(list = transform_list; list; list = list->next)
189                 len += lstrlenW(list->str) + 1;
190
191         ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
192
193         /* add all the transforms with a semicolon between each one */
194         p = ret;
195         for(list = transform_list; list; list = list->next)
196         {
197                 len = lstrlenW(list->str);
198                 lstrcpynW(p, list->str, len );
199                 p += len;
200                 if(list->next)
201                         *p++ = ';';
202         }
203         *p = 0;
204
205         return ret;
206 }
207
208 static DWORD msi_atou(LPCWSTR str)
209 {
210         DWORD ret = 0;
211         while(*str >= '0' && *str <= '9')
212         {
213                 ret *= 10;
214                 ret += (*str - '0');
215                 str++;
216         }
217         return ret;
218 }
219
220 static LPWSTR msi_strdup(LPCWSTR str)
221 {
222         DWORD len = lstrlenW(str)+1;
223         LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
224         lstrcpyW(ret, str);
225         return ret;
226 }
227
228 /* str1 is the same as str2, ignoring case */
229 static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
230 {
231         DWORD len, ret;
232         LPWSTR strW;
233
234         len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
235         if( !len )
236                 return FALSE;
237         if( lstrlenW(str1) != (len-1) )
238                 return FALSE;
239         strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
240         MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
241         ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len);
242         HeapFree(GetProcessHeap(), 0, strW);
243         return (ret == CSTR_EQUAL);
244 }
245
246 /* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
247 static BOOL msi_option_equal(LPCWSTR str1, LPCSTR str2)
248 {
249     if (str1[0] != '/' && str1[0] != '-')
250         return FALSE;
251
252     /* skip over the hyphen or slash */
253     return msi_strequal(str1 + 1, str2);
254 }
255
256 /* str2 is at the beginning of str1, ignoring case */
257 static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2)
258 {
259         DWORD len, ret;
260         LPWSTR strW;
261
262         len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
263         if( !len )
264                 return FALSE;
265         if( lstrlenW(str1) < (len-1) )
266                 return FALSE;
267         strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
268         MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
269         ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1);
270         HeapFree(GetProcessHeap(), 0, strW);
271         return (ret == CSTR_EQUAL);
272 }
273
274 /* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
275 static BOOL msi_option_prefix(LPCWSTR str1, LPCSTR str2)
276 {
277     if (str1[0] != '/' && str1[0] != '-')
278         return FALSE;
279
280     /* skip over the hyphen or slash */
281     return msi_strprefix(str1 + 1, str2);
282 }
283
284 static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
285 {
286         VOID* (*proc)(void);
287
288         *DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
289         if(!*DllHandle)
290         {
291                 fprintf(stderr, "Unable to load dll %s\n", wine_dbgstr_w(DllName));
292                 ExitProcess(1);
293         }
294         proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
295         if(!proc)
296         {
297                 fprintf(stderr, "Dll %s does not implement function %s\n",
298                         wine_dbgstr_w(DllName), ProcName);
299                 FreeLibrary(*DllHandle);
300                 ExitProcess(1);
301         }
302
303         return proc;
304 }
305
306 static DWORD DoDllRegisterServer(LPCWSTR DllName)
307 {
308         HRESULT hr;
309         DLLREGISTERSERVER pfDllRegisterServer = NULL;
310         HMODULE DllHandle = NULL;
311
312         pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
313
314         hr = pfDllRegisterServer();
315         if(FAILED(hr))
316         {
317                 fprintf(stderr, "Failed to register dll %s\n", wine_dbgstr_w(DllName));
318                 return 1;
319         }
320         printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
321         if(DllHandle)
322                 FreeLibrary(DllHandle);
323         return 0;
324 }
325
326 static DWORD DoDllUnregisterServer(LPCWSTR DllName)
327 {
328         HRESULT hr;
329         DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
330         HMODULE DllHandle = NULL;
331
332         pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
333
334         hr = pfDllUnregisterServer();
335         if(FAILED(hr))
336         {
337                 fprintf(stderr, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
338                 return 1;
339         }
340         printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
341         if(DllHandle)
342                 FreeLibrary(DllHandle);
343         return 0;
344 }
345
346 static DWORD DoRegServer(void)
347 {
348     SC_HANDLE scm, service;
349     CHAR path[MAX_PATH+12];
350     DWORD ret = 0;
351
352     scm = OpenSCManagerA(NULL, SERVICES_ACTIVE_DATABASEA, SC_MANAGER_CREATE_SERVICE);
353     if (!scm)
354     {
355         fprintf(stderr, "Failed to open the service control manager.\n");
356         return 1;
357     }
358
359     GetSystemDirectoryA(path, MAX_PATH);
360     lstrcatA(path, "\\msiexec.exe /V");
361
362     service = CreateServiceA(scm, "MSIServer", "MSIServer", GENERIC_ALL,
363                              SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START,
364                              SERVICE_ERROR_NORMAL, path, NULL, NULL,
365                              NULL, NULL, NULL);
366
367     if (service) CloseServiceHandle(service);
368     else if (GetLastError() != ERROR_SERVICE_EXISTS)
369     {
370         fprintf(stderr, "Failed to create MSI service\n");
371         ret = 1;
372     }
373     CloseServiceHandle(scm);
374     return ret;
375 }
376
377 static INT DoEmbedding( LPWSTR key )
378 {
379         printf("Remote custom actions are not supported yet\n");
380         return 1;
381 }
382
383 /*
384  * state machine to break up the command line properly
385  */
386
387 enum chomp_state
388 {
389         cs_whitespace,
390         cs_token,
391         cs_quote
392 };
393
394 static int chomp( WCHAR *str )
395 {
396         enum chomp_state state = cs_token;
397         WCHAR *p, *out;
398         int count = 1, ignore;
399
400         for( p = str, out = str; *p; p++ )
401         {
402                 ignore = 1;
403                 switch( state )
404                 {
405                 case cs_whitespace:
406                         switch( *p )
407                         {
408                         case ' ':
409                                 break;
410                         case '"':
411                                 state = cs_quote;
412                                 count++;
413                                 break;
414                         default:
415                                 count++;
416                                 ignore = 0;
417                                 state = cs_token;
418                         }
419                         break;
420
421                 case cs_token:
422                         switch( *p )
423                         {
424                         case '"':
425                                 state = cs_quote;
426                                 break;
427                         case ' ':
428                                 state = cs_whitespace;
429                                 *out++ = 0;
430                                 break;
431                         default:
432                                 ignore = 0;
433                         }
434                         break;
435
436                 case cs_quote:
437                         switch( *p )
438                         {
439                         case '"':
440                                 state = cs_token;
441                                 break;
442                         default:
443                                 ignore = 0;
444                         }
445                         break;
446                 }
447                 if( !ignore )
448                         *out++ = *p;
449         }
450
451         *out = 0;
452
453         return count;
454 }
455
456 static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
457 {
458         WCHAR **argv, *p = msi_strdup(cmdline);
459         int i, n;
460
461         n = chomp( p );
462         argv = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR*)*(n+1));
463         for( i=0; i<n; i++ )
464         {
465                 argv[i] = p;
466                 p += lstrlenW(p) + 1;
467         }
468         argv[i] = NULL;
469
470         *pargc = n;
471         *pargv = argv;
472 }
473
474 static BOOL process_args_from_reg( LPWSTR ident, int *pargc, WCHAR ***pargv )
475 {
476         LONG r;
477         HKEY hkey = 0, hkeyArgs = 0;
478         DWORD sz = 0, type = 0;
479         LPWSTR buf = NULL;
480         BOOL ret = FALSE;
481
482         r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
483         if(r != ERROR_SUCCESS)
484                 return FALSE;
485         r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
486         if(r == ERROR_SUCCESS && type == REG_SZ)
487         {
488                 buf = HeapAlloc(GetProcessHeap(), 0, sz);
489                 r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)buf, &sz);
490                 if( r == ERROR_SUCCESS )
491                 {
492                         process_args(buf, pargc, pargv);
493                         ret = TRUE;
494                 }
495                 HeapFree(GetProcessHeap(), 0, buf);
496         }
497         RegCloseKey(hkeyArgs);
498         return ret;
499 }
500
501 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
502 {
503         int i;
504         BOOL FunctionInstall = FALSE;
505         BOOL FunctionInstallAdmin = FALSE;
506         BOOL FunctionRepair = FALSE;
507         BOOL FunctionAdvertise = FALSE;
508         BOOL FunctionPatch = FALSE;
509         BOOL FunctionDllRegisterServer = FALSE;
510         BOOL FunctionDllUnregisterServer = FALSE;
511         BOOL FunctionRegServer = FALSE;
512         BOOL FunctionUnregServer = FALSE;
513         BOOL FunctionServer = FALSE;
514         BOOL FunctionUnknown = FALSE;
515
516         LPWSTR PackageName = NULL;
517         LPWSTR Properties = NULL;
518         struct string_list *property_list = NULL;
519
520         DWORD RepairMode = 0;
521
522         DWORD_PTR AdvertiseMode = 0;
523         struct string_list *transform_list = NULL;
524         LANGID Language = 0;
525
526         DWORD LogMode = 0;
527         LPWSTR LogFileName = NULL;
528         DWORD LogAttributes = 0;
529
530         LPWSTR PatchFileName = NULL;
531         INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
532
533         INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
534
535         LPWSTR DllName = NULL;
536         DWORD ReturnCode;
537         int argc;
538         LPWSTR *argvW = NULL;
539
540         /* parse the command line */
541         process_args( GetCommandLineW(), &argc, &argvW );
542
543         /*
544          * If the args begin with /@ IDENT then we need to load the real
545          * command line out of the RunOnceEntries key in the registry.
546          *  We do that before starting to process the real commandline,
547          * then overwrite the commandline again.
548          */
549         if(argc>1 && msi_option_equal(argvW[1], "@"))
550         {
551                 if(!process_args_from_reg( argvW[2], &argc, &argvW ))
552                         return 1;
553         }
554
555         if (argc == 3 && msi_option_equal(argvW[1], "Embedding"))
556                 return DoEmbedding( argvW[2] );
557
558         for(i = 1; i < argc; i++)
559         {
560                 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
561
562                 if (msi_option_equal(argvW[i], "regserver"))
563                 {
564                         FunctionRegServer = TRUE;
565                 }
566                 else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")
567                         ||  msi_option_equal(argvW[i], "unreg"))
568                 {
569                         FunctionUnregServer = TRUE;
570                 }
571                 else if(msi_option_prefix(argvW[i], "i") || msi_option_prefix(argvW[i], "package"))
572                 {
573                         LPWSTR argvWi = argvW[i];
574                         int argLen = (msi_option_prefix(argvW[i], "i") ? 2 : 8);
575                         FunctionInstall = TRUE;
576                         if(lstrlenW(argvW[i]) > argLen)
577                                 argvWi += argLen;
578                         else
579                         {
580                                 i++;
581                                 if(i >= argc)
582                                         ShowUsage(1);
583                                 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
584                                 argvWi = argvW[i];
585                         }
586                         PackageName = argvWi;
587                 }
588                 else if(msi_option_equal(argvW[i], "a"))
589                 {
590                         FunctionInstall = TRUE;
591                         FunctionInstallAdmin = TRUE;
592                         InstallType = INSTALLTYPE_NETWORK_IMAGE;
593                         i++;
594                         if(i >= argc)
595                                 ShowUsage(1);
596                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
597                         PackageName = argvW[i];
598                         StringListAppend(&property_list, ActionAdmin);
599                 }
600                 else if(msi_option_prefix(argvW[i], "f"))
601                 {
602                         int j;
603                         int len = lstrlenW(argvW[i]);
604                         FunctionRepair = TRUE;
605                         for(j = 2; j < len; j++)
606                         {
607                                 switch(argvW[i][j])
608                                 {
609                                         case 'P':
610                                         case 'p':
611                                                 RepairMode |= REINSTALLMODE_FILEMISSING;
612                                                 break;
613                                         case 'O':
614                                         case 'o':
615                                                 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
616                                                 break;
617                                         case 'E':
618                                         case 'e':
619                                                 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
620                                                 break;
621                                         case 'D':
622                                         case 'd':
623                                                 RepairMode |= REINSTALLMODE_FILEEXACT;
624                                                 break;
625                                         case 'C':
626                                         case 'c':
627                                                 RepairMode |= REINSTALLMODE_FILEVERIFY;
628                                                 break;
629                                         case 'A':
630                                         case 'a':
631                                                 RepairMode |= REINSTALLMODE_FILEREPLACE;
632                                                 break;
633                                         case 'U':
634                                         case 'u':
635                                                 RepairMode |= REINSTALLMODE_USERDATA;
636                                                 break;
637                                         case 'M':
638                                         case 'm':
639                                                 RepairMode |= REINSTALLMODE_MACHINEDATA;
640                                                 break;
641                                         case 'S':
642                                         case 's':
643                                                 RepairMode |= REINSTALLMODE_SHORTCUT;
644                                                 break;
645                                         case 'V':
646                                         case 'v':
647                                                 RepairMode |= REINSTALLMODE_PACKAGE;
648                                                 break;
649                                         default:
650                                                 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
651                                                 break;
652                                 }
653                         }
654                         if(len == 2)
655                         {
656                                 RepairMode = REINSTALLMODE_FILEMISSING |
657                                         REINSTALLMODE_FILEEQUALVERSION |
658                                         REINSTALLMODE_FILEVERIFY |
659                                         REINSTALLMODE_MACHINEDATA |
660                                         REINSTALLMODE_SHORTCUT;
661                         }
662                         i++;
663                         if(i >= argc)
664                                 ShowUsage(1);
665                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
666                         PackageName = argvW[i];
667                 }
668                 else if(msi_option_prefix(argvW[i], "x") || msi_option_equal(argvW[i], "uninstall"))
669                 {
670                         FunctionInstall = TRUE;
671                         if(msi_option_prefix(argvW[i], "x")) PackageName = argvW[i]+2;
672                         if(!PackageName || !PackageName[0])
673                         {
674                                 i++;
675                                 if (i >= argc)
676                                         ShowUsage(1);
677                                 PackageName = argvW[i];
678                         }
679                         WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName));
680                         StringListAppend(&property_list, RemoveAll);
681                 }
682                 else if(msi_option_prefix(argvW[i], "j"))
683                 {
684                         int j;
685                         int len = lstrlenW(argvW[i]);
686                         FunctionAdvertise = TRUE;
687                         for(j = 2; j < len; j++)
688                         {
689                                 switch(argvW[i][j])
690                                 {
691                                         case 'U':
692                                         case 'u':
693                                                 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
694                                                 break;
695                                         case 'M':
696                                         case 'm':
697                                                 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
698                                                 break;
699                                         default:
700                                                 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
701                                                 break;
702                                 }
703                         }
704                         i++;
705                         if(i >= argc)
706                                 ShowUsage(1);
707                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
708                         PackageName = argvW[i];
709                 }
710                 else if(msi_strequal(argvW[i], "u"))
711                 {
712                         FunctionAdvertise = TRUE;
713                         AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
714                         i++;
715                         if(i >= argc)
716                                 ShowUsage(1);
717                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
718                         PackageName = argvW[i];
719                 }
720                 else if(msi_strequal(argvW[i], "m"))
721                 {
722                         FunctionAdvertise = TRUE;
723                         AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
724                         i++;
725                         if(i >= argc)
726                                 ShowUsage(1);
727                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
728                         PackageName = argvW[i];
729                 }
730                 else if(msi_option_equal(argvW[i], "t"))
731                 {
732                         i++;
733                         if(i >= argc)
734                                 ShowUsage(1);
735                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
736                         StringListAppend(&transform_list, argvW[i]);
737                 }
738                 else if(msi_option_equal(argvW[i], "g"))
739                 {
740                         i++;
741                         if(i >= argc)
742                                 ShowUsage(1);
743                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
744                         Language = msi_atou(argvW[i]);
745                 }
746                 else if(msi_option_prefix(argvW[i], "l"))
747                 {
748                         int j;
749                         int len = lstrlenW(argvW[i]);
750                         for(j = 2; j < len; j++)
751                         {
752                                 switch(argvW[i][j])
753                                 {
754                                         case 'I':
755                                         case 'i':
756                                                 LogMode |= INSTALLLOGMODE_INFO;
757                                                 break;
758                                         case 'W':
759                                         case 'w':
760                                                 LogMode |= INSTALLLOGMODE_WARNING;
761                                                 break;
762                                         case 'E':
763                                         case 'e':
764                                                 LogMode |= INSTALLLOGMODE_ERROR;
765                                                 break;
766                                         case 'A':
767                                         case 'a':
768                                                 LogMode |= INSTALLLOGMODE_ACTIONSTART;
769                                                 break;
770                                         case 'R':
771                                         case 'r':
772                                                 LogMode |= INSTALLLOGMODE_ACTIONDATA;
773                                                 break;
774                                         case 'U':
775                                         case 'u':
776                                                 LogMode |= INSTALLLOGMODE_USER;
777                                                 break;
778                                         case 'C':
779                                         case 'c':
780                                                 LogMode |= INSTALLLOGMODE_COMMONDATA;
781                                                 break;
782                                         case 'M':
783                                         case 'm':
784                                                 LogMode |= INSTALLLOGMODE_FATALEXIT;
785                                                 break;
786                                         case 'O':
787                                         case 'o':
788                                                 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
789                                                 break;
790                                         case 'P':
791                                         case 'p':
792                                                 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
793                                                 break;
794                                         case 'V':
795                                         case 'v':
796                                                 LogMode |= INSTALLLOGMODE_VERBOSE;
797                                                 break;
798                                         case '*':
799                                                 LogMode = INSTALLLOGMODE_FATALEXIT |
800                                                         INSTALLLOGMODE_ERROR |
801                                                         INSTALLLOGMODE_WARNING |
802                                                         INSTALLLOGMODE_USER |
803                                                         INSTALLLOGMODE_INFO |
804                                                         INSTALLLOGMODE_RESOLVESOURCE |
805                                                         INSTALLLOGMODE_OUTOFDISKSPACE |
806                                                         INSTALLLOGMODE_ACTIONSTART |
807                                                         INSTALLLOGMODE_ACTIONDATA |
808                                                         INSTALLLOGMODE_COMMONDATA |
809                                                         INSTALLLOGMODE_PROPERTYDUMP |
810                                                         INSTALLLOGMODE_PROGRESS |
811                                                         INSTALLLOGMODE_INITIALIZE |
812                                                         INSTALLLOGMODE_TERMINATE |
813                                                         INSTALLLOGMODE_SHOWDIALOG;
814                                                 break;
815                                         case '+':
816                                                 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
817                                                 break;
818                                         case '!':
819                                                 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
820                                                 break;
821                                         default:
822                                                 break;
823                                 }
824                         }
825                         i++;
826                         if(i >= argc)
827                                 ShowUsage(1);
828                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
829                         LogFileName = argvW[i];
830                         if(MsiEnableLogW(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
831                         {
832                                 fprintf(stderr, "Logging in %s (0x%08x, %u) failed\n",
833                                          wine_dbgstr_w(LogFileName), LogMode, LogAttributes);
834                                 ExitProcess(1);
835                         }
836                 }
837                 else if(msi_option_equal(argvW[i], "p"))
838                 {
839                         FunctionPatch = TRUE;
840                         i++;
841                         if(i >= argc)
842                                 ShowUsage(1);
843                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
844                         PatchFileName = argvW[i];
845                 }
846                 else if(msi_option_prefix(argvW[i], "q"))
847                 {
848                         if(lstrlenW(argvW[i]) == 2 || msi_strequal(argvW[i]+2, "n") ||
849                            msi_strequal(argvW[i] + 2, "uiet"))
850                         {
851                                 InstallUILevel = INSTALLUILEVEL_NONE;
852                         }
853                         else if(msi_strequal(argvW[i]+2, "b"))
854                         {
855                                 InstallUILevel = INSTALLUILEVEL_BASIC;
856                         }
857                         else if(msi_strequal(argvW[i]+2, "r"))
858                         {
859                                 InstallUILevel = INSTALLUILEVEL_REDUCED;
860                         }
861                         else if(msi_strequal(argvW[i]+2, "f"))
862                         {
863                                 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
864                         }
865                         else if(msi_strequal(argvW[i]+2, "n+"))
866                         {
867                                 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
868                         }
869                         else if(msi_strequal(argvW[i]+2, "b+"))
870                         {
871                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
872                         }
873                         else if(msi_strequal(argvW[i]+2, "b-"))
874                         {
875                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
876                         }
877                         else if(msi_strequal(argvW[i]+2, "b+!"))
878                         {
879                                 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
880                                 WINE_FIXME("Unknown modifier: !\n");
881                         }
882                         else
883                         {
884                                 fprintf(stderr, "Unknown option \"%s\" for UI level\n",
885                                          wine_dbgstr_w(argvW[i]+2));
886                         }
887                 }
888                 else if(msi_option_equal(argvW[i], "y"))
889                 {
890                         FunctionDllRegisterServer = TRUE;
891                         i++;
892                         if(i >= argc)
893                                 ShowUsage(1);
894                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
895                         DllName = argvW[i];
896                 }
897                 else if(msi_option_equal(argvW[i], "z"))
898                 {
899                         FunctionDllUnregisterServer = TRUE;
900                         i++;
901                         if(i >= argc)
902                                 ShowUsage(1);
903                         WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
904                         DllName = argvW[i];
905                 }
906                 else if(msi_option_equal(argvW[i], "help") || msi_option_equal(argvW[i], "?"))
907                 {
908                         ShowUsage(0);
909                 }
910                 else if(msi_option_equal(argvW[i], "m"))
911                 {
912                         FunctionUnknown = TRUE;
913                         WINE_FIXME("Unknown parameter /m\n");
914                 }
915                 else if(msi_option_equal(argvW[i], "D"))
916                 {
917                         FunctionUnknown = TRUE;
918                         WINE_FIXME("Unknown parameter /D\n");
919                 }
920                 else if (msi_option_equal(argvW[i], "V"))
921                 {
922                     FunctionServer = TRUE;
923                 }
924                 else
925                         StringListAppend(&property_list, argvW[i]);
926         }
927
928         /* start the GUI */
929         MsiSetInternalUI(InstallUILevel, NULL);
930
931         Properties = build_properties( property_list );
932
933         if(FunctionInstallAdmin && FunctionPatch)
934                 FunctionInstall = FALSE;
935
936         ReturnCode = 1;
937         if(FunctionInstall)
938         {
939                 if(IsProductCode(PackageName))
940                         ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
941                 else
942                         ReturnCode = MsiInstallProductW(PackageName, Properties);
943         }
944         else if(FunctionRepair)
945         {
946                 if(IsProductCode(PackageName))
947                         WINE_FIXME("Product code treatment not implemented yet\n");
948                 else
949                         ReturnCode = MsiReinstallProductW(PackageName, RepairMode);
950         }
951         else if(FunctionAdvertise)
952         {
953                 LPWSTR Transforms = build_transforms( property_list );
954                 ReturnCode = MsiAdvertiseProductW(PackageName, (LPWSTR) AdvertiseMode, Transforms, Language);
955         }
956         else if(FunctionPatch)
957         {
958                 ReturnCode = MsiApplyPatchW(PatchFileName, PackageName, InstallType, Properties);
959         }
960         else if(FunctionDllRegisterServer)
961         {
962                 ReturnCode = DoDllRegisterServer(DllName);
963         }
964         else if(FunctionDllUnregisterServer)
965         {
966                 ReturnCode = DoDllUnregisterServer(DllName);
967         }
968         else if (FunctionRegServer)
969         {
970                 ReturnCode = DoRegServer();
971         }
972         else if (FunctionUnregServer)
973         {
974                 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
975         }
976         else if (FunctionServer)
977         {
978             ReturnCode = DoService();
979         }
980         else if (FunctionUnknown)
981         {
982                 WINE_FIXME( "Unknown function, ignoring\n" );
983         }
984         else
985                 ShowUsage(1);
986
987         return ReturnCode;
988 }