PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/base/system/msiexec/msiexec.c

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