bcrypt: Require macOS version 10.7 or newer for encryption support.
[wine.git] / dlls / msi / action.c
blob58a831e7e63c261bbab0ad37aa67a6f76fed1d4f
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "winuser.h"
34 #include "shlobj.h"
35 #include "objbase.h"
36 #include "mscoree.h"
37 #include "shlwapi.h"
38 #include "imagehlp.h"
39 #include "wine/unicode.h"
40 #include "winver.h"
42 #include "msipriv.h"
43 #include "resource.h"
45 #define REG_PROGRESS_VALUE 13200
46 #define COMPONENT_PROGRESS_VALUE 24000
48 WINE_DEFAULT_DEBUG_CHANNEL(msi);
50 static const WCHAR szCreateFolders[] =
51 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
52 static const WCHAR szCostFinalize[] =
53 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
54 static const WCHAR szWriteRegistryValues[] =
55 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
56 static const WCHAR szFileCost[] =
57 {'F','i','l','e','C','o','s','t',0};
58 static const WCHAR szInstallInitialize[] =
59 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
60 static const WCHAR szInstallValidate[] =
61 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
62 static const WCHAR szLaunchConditions[] =
63 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
64 static const WCHAR szProcessComponents[] =
65 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
66 static const WCHAR szRegisterTypeLibraries[] =
67 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
68 static const WCHAR szCreateShortcuts[] =
69 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
70 static const WCHAR szPublishProduct[] =
71 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
72 static const WCHAR szWriteIniValues[] =
73 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
74 static const WCHAR szSelfRegModules[] =
75 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
76 static const WCHAR szPublishFeatures[] =
77 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
78 static const WCHAR szRegisterProduct[] =
79 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
80 static const WCHAR szInstallExecute[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
82 static const WCHAR szInstallExecuteAgain[] =
83 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
84 static const WCHAR szInstallFinalize[] =
85 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
86 static const WCHAR szForceReboot[] =
87 {'F','o','r','c','e','R','e','b','o','o','t',0};
88 static const WCHAR szResolveSource[] =
89 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
90 static const WCHAR szAllocateRegistrySpace[] =
91 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
92 static const WCHAR szBindImage[] =
93 {'B','i','n','d','I','m','a','g','e',0};
94 static const WCHAR szDeleteServices[] =
95 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
96 static const WCHAR szDisableRollback[] =
97 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
98 static const WCHAR szExecuteAction[] =
99 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
100 static const WCHAR szInstallAdminPackage[] =
101 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
102 static const WCHAR szInstallSFPCatalogFile[] =
103 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
104 static const WCHAR szIsolateComponents[] =
105 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
106 static const WCHAR szMigrateFeatureStates[] =
107 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
108 static const WCHAR szMsiUnpublishAssemblies[] =
109 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
110 static const WCHAR szInstallODBC[] =
111 {'I','n','s','t','a','l','l','O','D','B','C',0};
112 static const WCHAR szInstallServices[] =
113 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
114 static const WCHAR szPublishComponents[] =
115 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
116 static const WCHAR szRegisterComPlus[] =
117 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
118 static const WCHAR szRegisterUser[] =
119 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
120 static const WCHAR szRemoveEnvironmentStrings[] =
121 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
122 static const WCHAR szRemoveExistingProducts[] =
123 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
124 static const WCHAR szRemoveFolders[] =
125 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
126 static const WCHAR szRemoveIniValues[] =
127 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
128 static const WCHAR szRemoveODBC[] =
129 {'R','e','m','o','v','e','O','D','B','C',0};
130 static const WCHAR szRemoveRegistryValues[] =
131 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
132 static const WCHAR szRemoveShortcuts[] =
133 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
134 static const WCHAR szRMCCPSearch[] =
135 {'R','M','C','C','P','S','e','a','r','c','h',0};
136 static const WCHAR szScheduleReboot[] =
137 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
138 static const WCHAR szSelfUnregModules[] =
139 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
140 static const WCHAR szSetODBCFolders[] =
141 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
142 static const WCHAR szStartServices[] =
143 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szStopServices[] =
145 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
146 static const WCHAR szUnpublishComponents[] =
147 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
148 static const WCHAR szUnpublishFeatures[] =
149 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
150 static const WCHAR szUnpublishProduct[] =
151 {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
152 static const WCHAR szUnregisterComPlus[] =
153 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
154 static const WCHAR szUnregisterTypeLibraries[] =
155 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
156 static const WCHAR szValidateProductID[] =
157 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
158 static const WCHAR szWriteEnvironmentStrings[] =
159 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
160 static const WCHAR szINSTALL[] =
161 {'I','N','S','T','A','L','L',0};
163 static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
165 WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
166 '`','A','c','t','i','o','n','T','e','x','t','`',' ','W','H','E','R','E',' ',
167 '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
168 MSIRECORD *row, *textrow;
169 INT rc;
171 textrow = MSI_QueryGetRecord(package->db, query, action);
172 if (textrow)
174 description = MSI_RecordGetString(textrow, 2);
175 template = MSI_RecordGetString(textrow, 3);
178 row = MSI_CreateRecord(3);
179 if (!row) return -1;
180 MSI_RecordSetStringW(row, 1, action);
181 MSI_RecordSetStringW(row, 2, description);
182 MSI_RecordSetStringW(row, 3, template);
183 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
184 if (textrow) msiobj_release(&textrow->hdr);
185 msiobj_release(&row->hdr);
186 return rc;
189 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
190 INT rc)
192 MSIRECORD *row;
193 WCHAR *template;
195 template = msi_get_error_message(package->db, start ? MSIERR_INFO_ACTIONSTART : MSIERR_INFO_ACTIONENDED);
197 row = MSI_CreateRecord(2);
198 if (!row)
200 msi_free(template);
201 return;
203 MSI_RecordSetStringW(row, 0, template);
204 MSI_RecordSetStringW(row, 1, action);
205 MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc);
206 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
207 msiobj_release(&row->hdr);
208 msi_free(template);
209 if (!start) package->LastActionResult = rc;
212 enum parse_state
214 state_whitespace,
215 state_token,
216 state_quote
219 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
221 enum parse_state state = state_quote;
222 const WCHAR *p;
223 WCHAR *out = value;
224 BOOL ignore, in_quotes = FALSE;
225 int count = 0, len = 0;
227 for (p = str; *p; p++)
229 ignore = FALSE;
230 switch (state)
232 case state_whitespace:
233 switch (*p)
235 case ' ':
236 in_quotes = TRUE;
237 ignore = TRUE;
238 len++;
239 break;
240 case '"':
241 state = state_quote;
242 if (in_quotes && p[1] != '\"') count--;
243 else count++;
244 break;
245 default:
246 state = state_token;
247 in_quotes = TRUE;
248 len++;
249 break;
251 break;
253 case state_token:
254 switch (*p)
256 case '"':
257 state = state_quote;
258 if (in_quotes) count--;
259 else count++;
260 break;
261 case ' ':
262 state = state_whitespace;
263 if (!count) goto done;
264 in_quotes = TRUE;
265 len++;
266 break;
267 default:
268 if (count) in_quotes = TRUE;
269 len++;
270 break;
272 break;
274 case state_quote:
275 switch (*p)
277 case '"':
278 if (in_quotes && p[1] != '\"') count--;
279 else count++;
280 break;
281 case ' ':
282 state = state_whitespace;
283 if (!count || (count > 1 && !len)) goto done;
284 in_quotes = TRUE;
285 len++;
286 break;
287 default:
288 state = state_token;
289 if (count) in_quotes = TRUE;
290 len++;
291 break;
293 break;
295 default: break;
297 if (!ignore) *out++ = *p;
298 if (!count) in_quotes = FALSE;
301 done:
302 if (!len) *value = 0;
303 else *out = 0;
305 *quotes = count;
306 return p - str;
309 static void remove_quotes( WCHAR *str )
311 WCHAR *p = str;
312 int len = strlenW( str );
314 while ((p = strchrW( p, '"' )))
316 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
317 p++;
321 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
322 BOOL preserve_case )
324 LPCWSTR ptr, ptr2;
325 int num_quotes;
326 DWORD len;
327 WCHAR *prop, *val;
328 UINT r;
330 if (!szCommandLine)
331 return ERROR_SUCCESS;
333 ptr = szCommandLine;
334 while (*ptr)
336 while (*ptr == ' ') ptr++;
337 if (!*ptr) break;
339 ptr2 = strchrW( ptr, '=' );
340 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
342 len = ptr2 - ptr;
343 if (!len) return ERROR_INVALID_COMMAND_LINE;
345 while (ptr[len - 1] == ' ') len--;
347 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
348 memcpy( prop, ptr, len * sizeof(WCHAR) );
349 prop[len] = 0;
350 if (!preserve_case) struprW( prop );
352 ptr2++;
353 while (*ptr2 == ' ') ptr2++;
355 num_quotes = 0;
356 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
357 len = parse_prop( ptr2, val, &num_quotes );
358 if (num_quotes % 2)
360 WARN("unbalanced quotes\n");
361 msi_free( val );
362 msi_free( prop );
363 return ERROR_INVALID_COMMAND_LINE;
365 remove_quotes( val );
366 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
368 r = msi_set_property( package->db, prop, val, -1 );
369 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
370 msi_reset_folders( package, TRUE );
372 msi_free( val );
373 msi_free( prop );
375 ptr = ptr2 + len;
378 return ERROR_SUCCESS;
381 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
383 LPCWSTR pc;
384 LPWSTR p, *ret = NULL;
385 UINT count = 0;
387 if (!str)
388 return ret;
390 /* count the number of substrings */
391 for ( pc = str, count = 0; pc; count++ )
393 pc = strchrW( pc, sep );
394 if (pc)
395 pc++;
398 /* allocate space for an array of substring pointers and the substrings */
399 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
400 (lstrlenW(str)+1) * sizeof(WCHAR) );
401 if (!ret)
402 return ret;
404 /* copy the string and set the pointers */
405 p = (LPWSTR) &ret[count+1];
406 lstrcpyW( p, str );
407 for( count = 0; (ret[count] = p); count++ )
409 p = strchrW( p, sep );
410 if (p)
411 *p++ = 0;
414 return ret;
417 static BOOL ui_sequence_exists( MSIPACKAGE *package )
419 static const WCHAR query [] = {
420 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
421 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
422 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
423 MSIQUERY *view;
424 DWORD count = 0;
426 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
428 MSI_IterateRecords( view, &count, NULL, package );
429 msiobj_release( &view->hdr );
431 return count != 0;
434 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
436 WCHAR *source, *check, *p, *db;
437 DWORD len;
439 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
440 return ERROR_OUTOFMEMORY;
442 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
444 msi_free(db);
445 return ERROR_SUCCESS;
447 len = p - db + 2;
448 source = msi_alloc( len * sizeof(WCHAR) );
449 lstrcpynW( source, db, len );
450 msi_free( db );
452 check = msi_dup_property( package->db, szSourceDir );
453 if (!check || replace)
455 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
456 if (r == ERROR_SUCCESS)
457 msi_reset_folders( package, TRUE );
459 msi_free( check );
461 check = msi_dup_property( package->db, szSOURCEDIR );
462 if (!check || replace)
463 msi_set_property( package->db, szSOURCEDIR, source, -1 );
465 msi_free( check );
466 msi_free( source );
468 return ERROR_SUCCESS;
471 static BOOL needs_ui_sequence(MSIPACKAGE *package)
473 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
476 UINT msi_set_context(MSIPACKAGE *package)
478 UINT r = msi_locate_product( package->ProductCode, &package->Context );
479 if (r != ERROR_SUCCESS)
481 int num = msi_get_property_int( package->db, szAllUsers, 0 );
482 if (num == 1 || num == 2)
483 package->Context = MSIINSTALLCONTEXT_MACHINE;
484 else
485 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
487 return ERROR_SUCCESS;
490 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
492 UINT rc;
493 LPCWSTR cond, action;
494 MSIPACKAGE *package = param;
496 action = MSI_RecordGetString(row,1);
497 if (!action)
499 ERR("Error is retrieving action name\n");
500 return ERROR_FUNCTION_FAILED;
503 /* check conditions */
504 cond = MSI_RecordGetString(row,2);
506 /* this is a hack to skip errors in the condition code */
507 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
509 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
510 return ERROR_SUCCESS;
513 rc = ACTION_PerformAction(package, action);
515 msi_dialog_check_messages( NULL );
517 if (rc == ERROR_FUNCTION_NOT_CALLED)
518 rc = ERROR_SUCCESS;
520 if (rc != ERROR_SUCCESS)
521 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
523 if (package->need_reboot_now)
525 TRACE("action %s asked for immediate reboot, suspending installation\n",
526 debugstr_w(action));
527 rc = ACTION_ForceReboot( package );
529 return rc;
532 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
534 static const WCHAR query[] = {
535 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
536 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
537 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
538 '`','S','e','q','u','e','n','c','e','`',0};
539 MSIQUERY *view;
540 UINT r;
542 TRACE("%p %s\n", package, debugstr_w(table));
544 r = MSI_OpenQuery( package->db, &view, query, table );
545 if (r == ERROR_SUCCESS)
547 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
548 msiobj_release(&view->hdr);
550 return r;
553 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package)
555 static const WCHAR query[] = {
556 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
557 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
558 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
559 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','0',' ',
560 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
561 MSIQUERY *view;
562 UINT rc;
564 if (package->ExecuteSequenceRun)
566 TRACE("Execute Sequence already Run\n");
567 return ERROR_SUCCESS;
570 package->ExecuteSequenceRun = TRUE;
572 rc = MSI_OpenQuery(package->db, &view, query);
573 if (rc == ERROR_SUCCESS)
575 TRACE("Running the actions\n");
577 msi_set_property( package->db, szSourceDir, NULL, -1 );
578 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
579 msiobj_release(&view->hdr);
581 return rc;
584 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
586 static const WCHAR query[] = {
587 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
588 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
589 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
590 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
591 MSIQUERY *view;
592 UINT rc;
594 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
595 if (rc == ERROR_SUCCESS)
597 TRACE("Running the actions\n");
598 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
599 msiobj_release(&view->hdr);
601 return rc;
604 /********************************************************
605 * ACTION helper functions and functions that perform the actions
606 *******************************************************/
607 static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action)
609 UINT arc;
610 INT uirc;
612 uirc = ui_actionstart(package, action, NULL, NULL);
613 if (uirc == IDCANCEL)
614 return ERROR_INSTALL_USEREXIT;
615 ui_actioninfo(package, action, TRUE, 0);
616 arc = ACTION_CustomAction(package, action);
617 uirc = !arc;
619 if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
621 uirc = ACTION_ShowDialog(package, action);
622 switch (uirc)
624 case -1:
625 return ERROR_SUCCESS; /* stop immediately */
626 case 0: arc = ERROR_FUNCTION_NOT_CALLED; break;
627 case 1: arc = ERROR_SUCCESS; break;
628 case 2: arc = ERROR_INSTALL_USEREXIT; break;
629 case 3: arc = ERROR_INSTALL_FAILURE; break;
630 case 4: arc = ERROR_INSTALL_SUSPEND; break;
631 case 5: arc = ERROR_MORE_DATA; break;
632 case 6: arc = ERROR_INVALID_HANDLE_STATE; break;
633 case 7: arc = ERROR_INVALID_DATA; break;
634 case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break;
635 case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break;
636 default: arc = ERROR_FUNCTION_FAILED; break;
640 ui_actioninfo(package, action, FALSE, uirc);
642 return arc;
645 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
647 MSICOMPONENT *comp;
649 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
651 if (!strcmpW( Component, comp->Component )) return comp;
653 return NULL;
656 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
658 MSIFEATURE *feature;
660 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
662 if (!strcmpW( Feature, feature->Feature )) return feature;
664 return NULL;
667 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
669 MSIFILE *file;
671 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
673 if (!strcmpW( key, file->File )) return file;
675 return NULL;
678 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
680 MSIFOLDER *folder;
682 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
684 if (!strcmpW( dir, folder->Directory )) return folder;
686 return NULL;
690 * Recursively create all directories in the path.
691 * shamelessly stolen from setupapi/queue.c
693 BOOL msi_create_full_path( const WCHAR *path )
695 BOOL ret = TRUE;
696 WCHAR *new_path;
697 int len;
699 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
700 strcpyW( new_path, path );
702 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
703 new_path[len - 1] = 0;
705 while (!CreateDirectoryW( new_path, NULL ))
707 WCHAR *slash;
708 DWORD last_error = GetLastError();
709 if (last_error == ERROR_ALREADY_EXISTS) break;
710 if (last_error != ERROR_PATH_NOT_FOUND)
712 ret = FALSE;
713 break;
715 if (!(slash = strrchrW( new_path, '\\' )))
717 ret = FALSE;
718 break;
720 len = slash - new_path;
721 new_path[len] = 0;
722 if (!msi_create_full_path( new_path ))
724 ret = FALSE;
725 break;
727 new_path[len] = '\\';
729 msi_free( new_path );
730 return ret;
733 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
735 MSIRECORD *row;
737 row = MSI_CreateRecord( 4 );
738 MSI_RecordSetInteger( row, 1, a );
739 MSI_RecordSetInteger( row, 2, b );
740 MSI_RecordSetInteger( row, 3, c );
741 MSI_RecordSetInteger( row, 4, d );
742 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
743 msiobj_release( &row->hdr );
745 msi_dialog_check_messages( NULL );
748 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
750 if (!comp->Enabled)
752 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
753 return INSTALLSTATE_UNKNOWN;
755 if (package->need_rollback) return comp->Installed;
756 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
758 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
759 return INSTALLSTATE_UNKNOWN;
761 return comp->ActionRequest;
764 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
766 if (package->need_rollback) return feature->Installed;
767 return feature->ActionRequest;
770 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
772 MSIPACKAGE *package = param;
773 LPCWSTR dir, component, full_path;
774 MSIRECORD *uirow;
775 MSIFOLDER *folder;
776 MSICOMPONENT *comp;
778 component = MSI_RecordGetString(row, 2);
779 if (!component)
780 return ERROR_SUCCESS;
782 comp = msi_get_loaded_component(package, component);
783 if (!comp)
784 return ERROR_SUCCESS;
786 comp->Action = msi_get_component_action( package, comp );
787 if (comp->Action != INSTALLSTATE_LOCAL)
789 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
790 return ERROR_SUCCESS;
793 dir = MSI_RecordGetString(row,1);
794 if (!dir)
796 ERR("Unable to get folder id\n");
797 return ERROR_SUCCESS;
800 uirow = MSI_CreateRecord(1);
801 MSI_RecordSetStringW(uirow, 1, dir);
802 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
803 msiobj_release(&uirow->hdr);
805 full_path = msi_get_target_folder( package, dir );
806 if (!full_path)
808 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
809 return ERROR_SUCCESS;
811 TRACE("folder is %s\n", debugstr_w(full_path));
813 folder = msi_get_loaded_folder( package, dir );
814 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
815 folder->State = FOLDER_STATE_CREATED;
816 return ERROR_SUCCESS;
819 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
821 static const WCHAR query[] = {
822 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
823 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
824 MSIQUERY *view;
825 UINT rc;
827 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
828 if (rc != ERROR_SUCCESS)
829 return ERROR_SUCCESS;
831 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
832 msiobj_release(&view->hdr);
833 return rc;
836 static void remove_persistent_folder( MSIFOLDER *folder )
838 FolderList *fl;
840 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
842 remove_persistent_folder( fl->folder );
844 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
846 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
850 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
852 MSIPACKAGE *package = param;
853 LPCWSTR dir, component, full_path;
854 MSIRECORD *uirow;
855 MSIFOLDER *folder;
856 MSICOMPONENT *comp;
858 component = MSI_RecordGetString(row, 2);
859 if (!component)
860 return ERROR_SUCCESS;
862 comp = msi_get_loaded_component(package, component);
863 if (!comp)
864 return ERROR_SUCCESS;
866 comp->Action = msi_get_component_action( package, comp );
867 if (comp->Action != INSTALLSTATE_ABSENT)
869 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
870 return ERROR_SUCCESS;
873 dir = MSI_RecordGetString( row, 1 );
874 if (!dir)
876 ERR("Unable to get folder id\n");
877 return ERROR_SUCCESS;
880 full_path = msi_get_target_folder( package, dir );
881 if (!full_path)
883 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
884 return ERROR_SUCCESS;
886 TRACE("folder is %s\n", debugstr_w(full_path));
888 uirow = MSI_CreateRecord( 1 );
889 MSI_RecordSetStringW( uirow, 1, dir );
890 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
891 msiobj_release( &uirow->hdr );
893 folder = msi_get_loaded_folder( package, dir );
894 remove_persistent_folder( folder );
895 return ERROR_SUCCESS;
898 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
900 static const WCHAR query[] = {
901 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
902 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
903 MSIQUERY *view;
904 UINT rc;
906 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
907 if (rc != ERROR_SUCCESS)
908 return ERROR_SUCCESS;
910 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
911 msiobj_release( &view->hdr );
912 return rc;
915 static UINT load_component( MSIRECORD *row, LPVOID param )
917 MSIPACKAGE *package = param;
918 MSICOMPONENT *comp;
920 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
921 if (!comp)
922 return ERROR_FUNCTION_FAILED;
924 list_add_tail( &package->components, &comp->entry );
926 /* fill in the data */
927 comp->Component = msi_dup_record_field( row, 1 );
929 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
931 comp->ComponentId = msi_dup_record_field( row, 2 );
932 comp->Directory = msi_dup_record_field( row, 3 );
933 comp->Attributes = MSI_RecordGetInteger(row,4);
934 comp->Condition = msi_dup_record_field( row, 5 );
935 comp->KeyPath = msi_dup_record_field( row, 6 );
937 comp->Installed = INSTALLSTATE_UNKNOWN;
938 comp->Action = INSTALLSTATE_UNKNOWN;
939 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
941 comp->assembly = msi_load_assembly( package, comp );
942 return ERROR_SUCCESS;
945 UINT msi_load_all_components( MSIPACKAGE *package )
947 static const WCHAR query[] = {
948 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
949 '`','C','o','m','p','o','n','e','n','t','`',0};
950 MSIQUERY *view;
951 UINT r;
953 if (!list_empty(&package->components))
954 return ERROR_SUCCESS;
956 r = MSI_DatabaseOpenViewW( package->db, query, &view );
957 if (r != ERROR_SUCCESS)
958 return r;
960 if (!msi_init_assembly_caches( package ))
962 ERR("can't initialize assembly caches\n");
963 msiobj_release( &view->hdr );
964 return ERROR_FUNCTION_FAILED;
967 r = MSI_IterateRecords(view, NULL, load_component, package);
968 msiobj_release(&view->hdr);
969 return r;
972 typedef struct {
973 MSIPACKAGE *package;
974 MSIFEATURE *feature;
975 } _ilfs;
977 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
979 ComponentList *cl;
981 cl = msi_alloc( sizeof (*cl) );
982 if ( !cl )
983 return ERROR_NOT_ENOUGH_MEMORY;
984 cl->component = comp;
985 list_add_tail( &feature->Components, &cl->entry );
987 return ERROR_SUCCESS;
990 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
992 FeatureList *fl;
994 fl = msi_alloc( sizeof(*fl) );
995 if ( !fl )
996 return ERROR_NOT_ENOUGH_MEMORY;
997 fl->feature = child;
998 list_add_tail( &parent->Children, &fl->entry );
1000 return ERROR_SUCCESS;
1003 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1005 _ilfs* ilfs = param;
1006 LPCWSTR component;
1007 MSICOMPONENT *comp;
1009 component = MSI_RecordGetString(row,1);
1011 /* check to see if the component is already loaded */
1012 comp = msi_get_loaded_component( ilfs->package, component );
1013 if (!comp)
1015 WARN("ignoring unknown component %s\n", debugstr_w(component));
1016 return ERROR_SUCCESS;
1018 add_feature_component( ilfs->feature, comp );
1019 comp->Enabled = TRUE;
1021 return ERROR_SUCCESS;
1024 static UINT load_feature(MSIRECORD * row, LPVOID param)
1026 static const WCHAR query[] = {
1027 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1028 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1029 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1030 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1031 MSIPACKAGE *package = param;
1032 MSIFEATURE *feature;
1033 MSIQUERY *view;
1034 _ilfs ilfs;
1035 UINT rc;
1037 /* fill in the data */
1039 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1040 if (!feature)
1041 return ERROR_NOT_ENOUGH_MEMORY;
1043 list_init( &feature->Children );
1044 list_init( &feature->Components );
1046 feature->Feature = msi_dup_record_field( row, 1 );
1048 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1050 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1051 feature->Title = msi_dup_record_field( row, 3 );
1052 feature->Description = msi_dup_record_field( row, 4 );
1054 if (!MSI_RecordIsNull(row,5))
1055 feature->Display = MSI_RecordGetInteger(row,5);
1057 feature->Level= MSI_RecordGetInteger(row,6);
1058 feature->Directory = msi_dup_record_field( row, 7 );
1059 feature->Attributes = MSI_RecordGetInteger(row,8);
1061 feature->Installed = INSTALLSTATE_UNKNOWN;
1062 feature->Action = INSTALLSTATE_UNKNOWN;
1063 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1065 list_add_tail( &package->features, &feature->entry );
1067 /* load feature components */
1069 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1070 if (rc != ERROR_SUCCESS)
1071 return ERROR_SUCCESS;
1073 ilfs.package = package;
1074 ilfs.feature = feature;
1076 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1077 msiobj_release(&view->hdr);
1078 return rc;
1081 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1083 MSIPACKAGE *package = param;
1084 MSIFEATURE *parent, *child;
1086 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1087 if (!child)
1088 return ERROR_FUNCTION_FAILED;
1090 if (!child->Feature_Parent)
1091 return ERROR_SUCCESS;
1093 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1094 if (!parent)
1095 return ERROR_FUNCTION_FAILED;
1097 add_feature_child( parent, child );
1098 return ERROR_SUCCESS;
1101 UINT msi_load_all_features( MSIPACKAGE *package )
1103 static const WCHAR query[] = {
1104 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1105 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1106 '`','D','i','s','p','l','a','y','`',0};
1107 MSIQUERY *view;
1108 UINT r;
1110 if (!list_empty(&package->features))
1111 return ERROR_SUCCESS;
1113 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1114 if (r != ERROR_SUCCESS)
1115 return r;
1117 r = MSI_IterateRecords( view, NULL, load_feature, package );
1118 if (r != ERROR_SUCCESS)
1120 msiobj_release( &view->hdr );
1121 return r;
1123 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1124 msiobj_release( &view->hdr );
1125 return r;
1128 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1130 if (!p)
1131 return p;
1132 p = strchrW(p, ch);
1133 if (!p)
1134 return p;
1135 *p = 0;
1136 return p+1;
1139 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1141 static const WCHAR query[] = {
1142 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1143 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1144 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1145 MSIQUERY *view = NULL;
1146 MSIRECORD *row = NULL;
1147 UINT r;
1149 TRACE("%s\n", debugstr_w(file->File));
1151 r = MSI_OpenQuery(package->db, &view, query, file->File);
1152 if (r != ERROR_SUCCESS)
1153 goto done;
1155 r = MSI_ViewExecute(view, NULL);
1156 if (r != ERROR_SUCCESS)
1157 goto done;
1159 r = MSI_ViewFetch(view, &row);
1160 if (r != ERROR_SUCCESS)
1161 goto done;
1163 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1164 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1165 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1166 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1167 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1169 done:
1170 if (view) msiobj_release(&view->hdr);
1171 if (row) msiobj_release(&row->hdr);
1172 return r;
1175 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1177 MSIRECORD *row;
1178 static const WCHAR query[] = {
1179 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1180 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1181 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1183 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1184 if (!row)
1186 WARN("query failed\n");
1187 return ERROR_FUNCTION_FAILED;
1190 file->disk_id = MSI_RecordGetInteger( row, 1 );
1191 msiobj_release( &row->hdr );
1192 return ERROR_SUCCESS;
1195 static UINT load_file(MSIRECORD *row, LPVOID param)
1197 MSIPACKAGE* package = param;
1198 LPCWSTR component;
1199 MSIFILE *file;
1201 /* fill in the data */
1203 file = msi_alloc_zero( sizeof (MSIFILE) );
1204 if (!file)
1205 return ERROR_NOT_ENOUGH_MEMORY;
1207 file->File = msi_dup_record_field( row, 1 );
1209 component = MSI_RecordGetString( row, 2 );
1210 file->Component = msi_get_loaded_component( package, component );
1212 if (!file->Component)
1214 WARN("Component not found: %s\n", debugstr_w(component));
1215 msi_free(file->File);
1216 msi_free(file);
1217 return ERROR_SUCCESS;
1220 file->FileName = msi_dup_record_field( row, 3 );
1221 msi_reduce_to_long_filename( file->FileName );
1223 file->ShortName = msi_dup_record_field( row, 3 );
1224 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1226 file->FileSize = MSI_RecordGetInteger( row, 4 );
1227 file->Version = msi_dup_record_field( row, 5 );
1228 file->Language = msi_dup_record_field( row, 6 );
1229 file->Attributes = MSI_RecordGetInteger( row, 7 );
1230 file->Sequence = MSI_RecordGetInteger( row, 8 );
1232 file->state = msifs_invalid;
1234 /* if the compressed bits are not set in the file attributes,
1235 * then read the information from the package word count property
1237 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1239 file->IsCompressed = FALSE;
1241 else if (file->Attributes &
1242 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1244 file->IsCompressed = TRUE;
1246 else if (file->Attributes & msidbFileAttributesNoncompressed)
1248 file->IsCompressed = FALSE;
1250 else
1252 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1255 load_file_hash(package, file);
1256 load_file_disk_id(package, file);
1258 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1260 list_add_tail( &package->files, &file->entry );
1262 return ERROR_SUCCESS;
1265 static UINT load_all_files(MSIPACKAGE *package)
1267 static const WCHAR query[] = {
1268 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1269 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1270 '`','S','e','q','u','e','n','c','e','`', 0};
1271 MSIQUERY *view;
1272 UINT rc;
1274 if (!list_empty(&package->files))
1275 return ERROR_SUCCESS;
1277 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1278 if (rc != ERROR_SUCCESS)
1279 return ERROR_SUCCESS;
1281 rc = MSI_IterateRecords(view, NULL, load_file, package);
1282 msiobj_release(&view->hdr);
1283 return rc;
1286 static UINT load_media( MSIRECORD *row, LPVOID param )
1288 MSIPACKAGE *package = param;
1289 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1290 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1292 /* FIXME: load external cabinets and directory sources too */
1293 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1294 return ERROR_SUCCESS;
1296 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1299 static UINT load_all_media( MSIPACKAGE *package )
1301 static const WCHAR query[] = {
1302 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1303 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1304 '`','D','i','s','k','I','d','`',0};
1305 MSIQUERY *view;
1306 UINT r;
1308 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1309 if (r != ERROR_SUCCESS)
1310 return ERROR_SUCCESS;
1312 r = MSI_IterateRecords( view, NULL, load_media, package );
1313 msiobj_release( &view->hdr );
1314 return r;
1317 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1319 static const WCHAR query[] =
1320 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1321 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1322 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1323 MSIRECORD *rec;
1325 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1327 WARN("query failed\n");
1328 return ERROR_FUNCTION_FAILED;
1331 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1332 msiobj_release( &rec->hdr );
1333 return ERROR_SUCCESS;
1336 static UINT load_patch(MSIRECORD *row, LPVOID param)
1338 MSIPACKAGE *package = param;
1339 MSIFILEPATCH *patch;
1340 const WCHAR *file_key;
1342 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1343 if (!patch)
1344 return ERROR_NOT_ENOUGH_MEMORY;
1346 file_key = MSI_RecordGetString( row, 1 );
1347 patch->File = msi_get_loaded_file( package, file_key );
1348 if (!patch->File)
1350 ERR("Failed to find target for patch in File table\n");
1351 msi_free(patch);
1352 return ERROR_FUNCTION_FAILED;
1355 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1356 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1357 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1359 /* FIXME:
1360 * Header field - for patch validation.
1361 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1364 load_patch_disk_id( package, patch );
1366 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1368 list_add_tail( &package->filepatches, &patch->entry );
1370 return ERROR_SUCCESS;
1373 static UINT load_all_patches(MSIPACKAGE *package)
1375 static const WCHAR query[] = {
1376 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1377 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1378 '`','S','e','q','u','e','n','c','e','`',0};
1379 MSIQUERY *view;
1380 UINT rc;
1382 if (!list_empty(&package->filepatches))
1383 return ERROR_SUCCESS;
1385 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1386 if (rc != ERROR_SUCCESS)
1387 return ERROR_SUCCESS;
1389 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1390 msiobj_release(&view->hdr);
1391 return rc;
1394 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1396 static const WCHAR query[] = {
1397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1398 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1399 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1400 MSIQUERY *view;
1402 folder->persistent = FALSE;
1403 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1405 if (!MSI_ViewExecute( view, NULL ))
1407 MSIRECORD *rec;
1408 if (!MSI_ViewFetch( view, &rec ))
1410 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1411 folder->persistent = TRUE;
1412 msiobj_release( &rec->hdr );
1415 msiobj_release( &view->hdr );
1417 return ERROR_SUCCESS;
1420 static UINT load_folder( MSIRECORD *row, LPVOID param )
1422 MSIPACKAGE *package = param;
1423 static WCHAR szEmpty[] = { 0 };
1424 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1425 MSIFOLDER *folder;
1427 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1428 list_init( &folder->children );
1429 folder->Directory = msi_dup_record_field( row, 1 );
1430 folder->Parent = msi_dup_record_field( row, 2 );
1431 p = msi_dup_record_field(row, 3);
1433 TRACE("%s\n", debugstr_w(folder->Directory));
1435 /* split src and target dir */
1436 tgt_short = p;
1437 src_short = folder_split_path( p, ':' );
1439 /* split the long and short paths */
1440 tgt_long = folder_split_path( tgt_short, '|' );
1441 src_long = folder_split_path( src_short, '|' );
1443 /* check for no-op dirs */
1444 if (tgt_short && !strcmpW( szDot, tgt_short ))
1445 tgt_short = szEmpty;
1446 if (src_short && !strcmpW( szDot, src_short ))
1447 src_short = szEmpty;
1449 if (!tgt_long)
1450 tgt_long = tgt_short;
1452 if (!src_short) {
1453 src_short = tgt_short;
1454 src_long = tgt_long;
1457 if (!src_long)
1458 src_long = src_short;
1460 /* FIXME: use the target short path too */
1461 folder->TargetDefault = strdupW(tgt_long);
1462 folder->SourceShortPath = strdupW(src_short);
1463 folder->SourceLongPath = strdupW(src_long);
1464 msi_free(p);
1466 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1467 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1468 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1470 load_folder_persistence( package, folder );
1472 list_add_tail( &package->folders, &folder->entry );
1473 return ERROR_SUCCESS;
1476 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1478 FolderList *fl;
1480 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1481 fl->folder = child;
1482 list_add_tail( &parent->children, &fl->entry );
1483 return ERROR_SUCCESS;
1486 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1488 MSIPACKAGE *package = param;
1489 MSIFOLDER *parent, *child;
1491 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1492 return ERROR_FUNCTION_FAILED;
1494 if (!child->Parent) return ERROR_SUCCESS;
1496 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1497 return ERROR_FUNCTION_FAILED;
1499 return add_folder_child( parent, child );
1502 static UINT load_all_folders( MSIPACKAGE *package )
1504 static const WCHAR query[] = {
1505 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1506 '`','D','i','r','e','c','t','o','r','y','`',0};
1507 MSIQUERY *view;
1508 UINT r;
1510 if (!list_empty(&package->folders))
1511 return ERROR_SUCCESS;
1513 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1514 if (r != ERROR_SUCCESS)
1515 return r;
1517 r = MSI_IterateRecords( view, NULL, load_folder, package );
1518 if (r != ERROR_SUCCESS)
1520 msiobj_release( &view->hdr );
1521 return r;
1523 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1524 msiobj_release( &view->hdr );
1525 return r;
1528 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1530 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1531 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1533 load_all_folders( package );
1534 msi_load_all_components( package );
1535 msi_load_all_features( package );
1536 load_all_files( package );
1537 load_all_patches( package );
1538 load_all_media( package );
1540 return ERROR_SUCCESS;
1543 static UINT execute_script( MSIPACKAGE *package, UINT script )
1545 UINT i, rc = ERROR_SUCCESS;
1547 TRACE("executing script %u\n", script);
1549 package->script = script;
1551 if (script == SCRIPT_ROLLBACK)
1553 for (i = package->script_actions_count[script]; i > 0; i--)
1555 rc = ACTION_PerformAction(package, package->script_actions[script][i-1]);
1556 if (rc != ERROR_SUCCESS)
1558 ERR("Execution of script %i halted; action %s returned %u\n",
1559 script, debugstr_w(package->script_actions[script][i-1]), rc);
1560 break;
1564 else
1566 for (i = 0; i < package->script_actions_count[script]; i++)
1568 rc = ACTION_PerformAction(package, package->script_actions[script][i]);
1569 if (rc != ERROR_SUCCESS)
1571 ERR("Execution of script %i halted; action %s returned %u\n",
1572 script, debugstr_w(package->script_actions[script][i]), rc);
1573 break;
1578 package->script = SCRIPT_NONE;
1580 msi_free_action_script(package, script);
1581 return rc;
1584 static UINT ACTION_FileCost(MSIPACKAGE *package)
1586 return ERROR_SUCCESS;
1589 static void get_client_counts( MSIPACKAGE *package )
1591 MSICOMPONENT *comp;
1592 HKEY hkey;
1594 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1596 if (!comp->ComponentId) continue;
1598 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1599 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1601 comp->num_clients = 0;
1602 continue;
1604 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1605 NULL, NULL, NULL, NULL );
1606 RegCloseKey( hkey );
1610 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1612 MSICOMPONENT *comp;
1613 UINT r;
1615 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1617 if (!comp->ComponentId) continue;
1619 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1620 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1621 &comp->Installed );
1622 if (r == ERROR_SUCCESS) continue;
1624 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1625 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1626 &comp->Installed );
1627 if (r == ERROR_SUCCESS) continue;
1629 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1630 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1631 &comp->Installed );
1632 if (r == ERROR_SUCCESS) continue;
1634 comp->Installed = INSTALLSTATE_ABSENT;
1638 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1640 MSIFEATURE *feature;
1642 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1644 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1646 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1647 feature->Installed = INSTALLSTATE_ABSENT;
1648 else
1649 feature->Installed = state;
1653 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1655 return (feature->Level > 0 && feature->Level <= level);
1658 static BOOL process_state_property(MSIPACKAGE* package, int level,
1659 LPCWSTR property, INSTALLSTATE state)
1661 LPWSTR override;
1662 MSIFEATURE *feature;
1663 BOOL remove = !strcmpW(property, szRemove);
1664 BOOL reinstall = !strcmpW(property, szReinstall);
1666 override = msi_dup_property( package->db, property );
1667 if (!override)
1668 return FALSE;
1670 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1672 if (feature->Level <= 0)
1673 continue;
1675 if (reinstall)
1676 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1677 else if (remove)
1678 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1680 if (!strcmpiW( override, szAll ))
1682 feature->Action = state;
1683 feature->ActionRequest = state;
1685 else
1687 LPWSTR ptr = override;
1688 LPWSTR ptr2 = strchrW(override,',');
1690 while (ptr)
1692 int len = ptr2 - ptr;
1694 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1695 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1697 feature->Action = state;
1698 feature->ActionRequest = state;
1699 break;
1701 if (ptr2)
1703 ptr=ptr2+1;
1704 ptr2 = strchrW(ptr,',');
1706 else
1707 break;
1711 msi_free(override);
1712 return TRUE;
1715 static BOOL process_overrides( MSIPACKAGE *package, int level )
1717 static const WCHAR szAddLocal[] =
1718 {'A','D','D','L','O','C','A','L',0};
1719 static const WCHAR szAddSource[] =
1720 {'A','D','D','S','O','U','R','C','E',0};
1721 static const WCHAR szAdvertise[] =
1722 {'A','D','V','E','R','T','I','S','E',0};
1723 BOOL ret = FALSE;
1725 /* all these activation/deactivation things happen in order and things
1726 * later on the list override things earlier on the list.
1728 * 0 INSTALLLEVEL processing
1729 * 1 ADDLOCAL
1730 * 2 REMOVE
1731 * 3 ADDSOURCE
1732 * 4 ADDDEFAULT
1733 * 5 REINSTALL
1734 * 6 ADVERTISE
1735 * 7 COMPADDLOCAL
1736 * 8 COMPADDSOURCE
1737 * 9 FILEADDLOCAL
1738 * 10 FILEADDSOURCE
1739 * 11 FILEADDDEFAULT
1741 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1742 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1743 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1744 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1745 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1747 if (ret)
1748 msi_set_property( package->db, szPreselected, szOne, -1 );
1750 return ret;
1753 static void disable_children( MSIFEATURE *feature, int level )
1755 FeatureList *fl;
1757 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1759 if (!is_feature_selected( feature, level ))
1761 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1762 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1763 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1765 fl->feature->Level = feature->Level;
1766 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1767 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1769 disable_children( fl->feature, level );
1773 static void follow_parent( MSIFEATURE *feature )
1775 FeatureList *fl;
1777 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1779 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1781 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1782 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1783 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1785 fl->feature->Action = feature->Action;
1786 fl->feature->ActionRequest = feature->ActionRequest;
1788 follow_parent( fl->feature );
1792 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1794 int level;
1795 MSICOMPONENT* component;
1796 MSIFEATURE *feature;
1798 TRACE("Checking Install Level\n");
1800 level = msi_get_property_int(package->db, szInstallLevel, 1);
1802 if (msi_get_property_int( package->db, szPreselected, 0 ))
1804 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1806 if (!is_feature_selected( feature, level )) continue;
1808 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1810 if (feature->Installed == INSTALLSTATE_ABSENT)
1812 feature->Action = INSTALLSTATE_UNKNOWN;
1813 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1815 else
1817 feature->Action = feature->Installed;
1818 feature->ActionRequest = feature->Installed;
1822 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1824 if (feature->Feature_Parent) continue;
1825 disable_children( feature, level );
1826 follow_parent( feature );
1829 else if (!msi_get_property_int( package->db, szInstalled, 0 ))
1831 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1833 if (!is_feature_selected( feature, level )) continue;
1835 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1837 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1839 feature->Action = INSTALLSTATE_SOURCE;
1840 feature->ActionRequest = INSTALLSTATE_SOURCE;
1842 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1844 feature->Action = INSTALLSTATE_ADVERTISED;
1845 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1847 else
1849 feature->Action = INSTALLSTATE_LOCAL;
1850 feature->ActionRequest = INSTALLSTATE_LOCAL;
1854 /* disable child features of unselected parent or follow parent */
1855 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1857 if (feature->Feature_Parent) continue;
1858 disable_children( feature, level );
1859 follow_parent( feature );
1863 /* now we want to set component state based based on feature state */
1864 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1866 ComponentList *cl;
1868 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1869 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1870 feature->ActionRequest, feature->Action);
1872 /* features with components that have compressed files are made local */
1873 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1875 if (cl->component->ForceLocalState &&
1876 feature->ActionRequest == INSTALLSTATE_SOURCE)
1878 feature->Action = INSTALLSTATE_LOCAL;
1879 feature->ActionRequest = INSTALLSTATE_LOCAL;
1880 break;
1884 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1886 component = cl->component;
1888 switch (feature->ActionRequest)
1890 case INSTALLSTATE_ABSENT:
1891 component->anyAbsent = 1;
1892 break;
1893 case INSTALLSTATE_ADVERTISED:
1894 component->hasAdvertisedFeature = 1;
1895 break;
1896 case INSTALLSTATE_SOURCE:
1897 component->hasSourceFeature = 1;
1898 break;
1899 case INSTALLSTATE_LOCAL:
1900 component->hasLocalFeature = 1;
1901 break;
1902 case INSTALLSTATE_DEFAULT:
1903 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1904 component->hasAdvertisedFeature = 1;
1905 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1906 component->hasSourceFeature = 1;
1907 else
1908 component->hasLocalFeature = 1;
1909 break;
1910 case INSTALLSTATE_UNKNOWN:
1911 if (feature->Installed == INSTALLSTATE_ADVERTISED)
1912 component->hasAdvertisedFeature = 1;
1913 if (feature->Installed == INSTALLSTATE_SOURCE)
1914 component->hasSourceFeature = 1;
1915 if (feature->Installed == INSTALLSTATE_LOCAL)
1916 component->hasLocalFeature = 1;
1917 break;
1918 default:
1919 break;
1924 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1926 /* check if it's local or source */
1927 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1928 (component->hasLocalFeature || component->hasSourceFeature))
1930 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1931 !component->ForceLocalState)
1933 component->Action = INSTALLSTATE_SOURCE;
1934 component->ActionRequest = INSTALLSTATE_SOURCE;
1936 else
1938 component->Action = INSTALLSTATE_LOCAL;
1939 component->ActionRequest = INSTALLSTATE_LOCAL;
1941 continue;
1944 /* if any feature is local, the component must be local too */
1945 if (component->hasLocalFeature)
1947 component->Action = INSTALLSTATE_LOCAL;
1948 component->ActionRequest = INSTALLSTATE_LOCAL;
1949 continue;
1951 if (component->hasSourceFeature)
1953 component->Action = INSTALLSTATE_SOURCE;
1954 component->ActionRequest = INSTALLSTATE_SOURCE;
1955 continue;
1957 if (component->hasAdvertisedFeature)
1959 component->Action = INSTALLSTATE_ADVERTISED;
1960 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1961 continue;
1963 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1964 if (component->anyAbsent && component->ComponentId)
1966 component->Action = INSTALLSTATE_ABSENT;
1967 component->ActionRequest = INSTALLSTATE_ABSENT;
1971 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1973 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1975 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1976 component->Action = INSTALLSTATE_LOCAL;
1977 component->ActionRequest = INSTALLSTATE_LOCAL;
1980 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1981 component->Installed == INSTALLSTATE_SOURCE &&
1982 component->hasSourceFeature)
1984 component->Action = INSTALLSTATE_UNKNOWN;
1985 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1988 TRACE("component %s (installed %d request %d action %d)\n",
1989 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1991 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1992 component->num_clients++;
1993 else if (component->Action == INSTALLSTATE_ABSENT)
1994 component->num_clients--;
1997 return ERROR_SUCCESS;
2000 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2002 MSIPACKAGE *package = param;
2003 LPCWSTR name;
2004 MSIFEATURE *feature;
2006 name = MSI_RecordGetString( row, 1 );
2008 feature = msi_get_loaded_feature( package, name );
2009 if (!feature)
2010 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2011 else
2013 LPCWSTR Condition;
2014 Condition = MSI_RecordGetString(row,3);
2016 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2018 int level = MSI_RecordGetInteger(row,2);
2019 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2020 feature->Level = level;
2023 return ERROR_SUCCESS;
2026 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2028 static const WCHAR name[] = {'\\',0};
2029 VS_FIXEDFILEINFO *ptr, *ret;
2030 LPVOID version;
2031 DWORD versize, handle;
2032 UINT sz;
2034 versize = GetFileVersionInfoSizeW( filename, &handle );
2035 if (!versize)
2036 return NULL;
2038 version = msi_alloc( versize );
2039 if (!version)
2040 return NULL;
2042 GetFileVersionInfoW( filename, 0, versize, version );
2044 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2046 msi_free( version );
2047 return NULL;
2050 ret = msi_alloc( sz );
2051 memcpy( ret, ptr, sz );
2053 msi_free( version );
2054 return ret;
2057 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2059 DWORD ms, ls;
2061 msi_parse_version_string( version, &ms, &ls );
2063 if (fi->dwFileVersionMS > ms) return 1;
2064 else if (fi->dwFileVersionMS < ms) return -1;
2065 else if (fi->dwFileVersionLS > ls) return 1;
2066 else if (fi->dwFileVersionLS < ls) return -1;
2067 return 0;
2070 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2072 DWORD ms1, ms2;
2074 msi_parse_version_string( ver1, &ms1, NULL );
2075 msi_parse_version_string( ver2, &ms2, NULL );
2077 if (ms1 > ms2) return 1;
2078 else if (ms1 < ms2) return -1;
2079 return 0;
2082 DWORD msi_get_disk_file_size( LPCWSTR filename )
2084 HANDLE file;
2085 DWORD size;
2087 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2088 if (file == INVALID_HANDLE_VALUE)
2089 return INVALID_FILE_SIZE;
2091 size = GetFileSize( file, NULL );
2092 CloseHandle( file );
2093 return size;
2096 BOOL msi_file_hash_matches( MSIFILE *file )
2098 UINT r;
2099 MSIFILEHASHINFO hash;
2101 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2102 r = msi_get_filehash( file->TargetPath, &hash );
2103 if (r != ERROR_SUCCESS)
2104 return FALSE;
2106 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2109 static WCHAR *create_temp_dir( MSIDATABASE *db )
2111 static UINT id;
2112 WCHAR *ret;
2114 if (!db->tempfolder)
2116 WCHAR tmp[MAX_PATH];
2117 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2119 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2120 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2122 GetTempPathW( MAX_PATH, tmp );
2124 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2127 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2129 for (;;)
2131 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2133 msi_free( ret );
2134 return NULL;
2136 if (CreateDirectoryW( ret, NULL )) break;
2140 return ret;
2144 * msi_build_directory_name()
2146 * This function is to save messing round with directory names
2147 * It handles adding backslashes between path segments,
2148 * and can add \ at the end of the directory name if told to.
2150 * It takes a variable number of arguments.
2151 * It always allocates a new string for the result, so make sure
2152 * to free the return value when finished with it.
2154 * The first arg is the number of path segments that follow.
2155 * The arguments following count are a list of path segments.
2156 * A path segment may be NULL.
2158 * Path segments will be added with a \ separating them.
2159 * A \ will not be added after the last segment, however if the
2160 * last segment is NULL, then the last character will be a \
2162 WCHAR *msi_build_directory_name( DWORD count, ... )
2164 DWORD sz = 1, i;
2165 WCHAR *dir;
2166 va_list va;
2168 va_start( va, count );
2169 for (i = 0; i < count; i++)
2171 const WCHAR *str = va_arg( va, const WCHAR * );
2172 if (str) sz += strlenW( str ) + 1;
2174 va_end( va );
2176 dir = msi_alloc( sz * sizeof(WCHAR) );
2177 dir[0] = 0;
2179 va_start( va, count );
2180 for (i = 0; i < count; i++)
2182 const WCHAR *str = va_arg( va, const WCHAR * );
2183 if (!str) continue;
2184 strcatW( dir, str );
2185 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2187 va_end( va );
2188 return dir;
2191 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2193 return comp->assembly && !comp->assembly->application;
2196 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2198 msi_free( file->TargetPath );
2199 if (msi_is_global_assembly( file->Component ))
2201 MSIASSEMBLY *assembly = file->Component->assembly;
2203 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2204 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2206 else
2208 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2209 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2212 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2215 static UINT calculate_file_cost( MSIPACKAGE *package )
2217 VS_FIXEDFILEINFO *file_version;
2218 WCHAR *font_version;
2219 MSIFILE *file;
2221 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2223 MSICOMPONENT *comp = file->Component;
2224 DWORD file_size;
2226 if (!comp->Enabled) continue;
2228 if (file->IsCompressed)
2229 comp->ForceLocalState = TRUE;
2231 set_target_path( package, file );
2233 if ((comp->assembly && !comp->assembly->installed) ||
2234 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2236 comp->Cost += file->FileSize;
2237 continue;
2239 file_size = msi_get_disk_file_size( file->TargetPath );
2240 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2242 if (file->Version)
2244 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2246 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2248 comp->Cost += file->FileSize - file_size;
2250 msi_free( file_version );
2251 continue;
2253 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2255 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2257 comp->Cost += file->FileSize - file_size;
2259 msi_free( font_version );
2260 continue;
2263 if (file_size != file->FileSize)
2265 comp->Cost += file->FileSize - file_size;
2268 return ERROR_SUCCESS;
2271 WCHAR *msi_normalize_path( const WCHAR *in )
2273 const WCHAR *p = in;
2274 WCHAR *q, *ret;
2275 int n, len = strlenW( in ) + 2;
2277 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2279 len = 0;
2280 while (1)
2282 /* copy until the end of the string or a space */
2283 while (*p != ' ' && (*q = *p))
2285 p++, len++;
2286 /* reduce many backslashes to one */
2287 if (*p != '\\' || *q != '\\')
2288 q++;
2291 /* quit at the end of the string */
2292 if (!*p)
2293 break;
2295 /* count the number of spaces */
2296 n = 0;
2297 while (p[n] == ' ')
2298 n++;
2300 /* if it's leading or trailing space, skip it */
2301 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2302 p += n;
2303 else /* copy n spaces */
2304 while (n && (*q++ = *p++)) n--;
2306 while (q - ret > 0 && q[-1] == ' ') q--;
2307 if (q - ret > 0 && q[-1] != '\\')
2309 q[0] = '\\';
2310 q[1] = 0;
2312 return ret;
2315 static WCHAR *get_install_location( MSIPACKAGE *package )
2317 HKEY hkey;
2318 WCHAR *path;
2320 if (!package->ProductCode) return NULL;
2321 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2322 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2324 msi_free( path );
2325 path = NULL;
2327 RegCloseKey( hkey );
2328 return path;
2331 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2333 FolderList *fl;
2334 MSIFOLDER *folder, *parent, *child;
2335 WCHAR *path, *normalized_path;
2337 TRACE("resolving %s\n", debugstr_w(name));
2339 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2341 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2343 if (!(path = get_install_location( package )) &&
2344 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2346 path = msi_dup_property( package->db, szRootDrive );
2349 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2351 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2353 parent = msi_get_loaded_folder( package, folder->Parent );
2354 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2356 else
2357 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2359 normalized_path = msi_normalize_path( path );
2360 msi_free( path );
2361 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2363 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2364 msi_free( normalized_path );
2365 return;
2367 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2368 msi_free( folder->ResolvedTarget );
2369 folder->ResolvedTarget = normalized_path;
2371 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2373 child = fl->folder;
2374 msi_resolve_target_folder( package, child->Directory, load_prop );
2376 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2379 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2381 MSICOMPONENT *comp;
2382 ULONGLONG ret = 0;
2384 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2386 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2388 return ret;
2391 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2393 static const WCHAR query[] =
2394 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2395 '`','C','o','n','d','i','t','i','o','n','`',0};
2396 static const WCHAR szOutOfDiskSpace[] =
2397 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2398 static const WCHAR szPrimaryFolder[] =
2399 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2400 static const WCHAR szPrimaryVolumePath[] =
2401 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2402 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2403 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2404 'A','v','a','i','l','a','b','l','e',0};
2405 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2406 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2407 'R','e','q','u','i','r','e','d',0};
2408 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2409 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2410 'R','e','m','a','i','n','i','n','g',0};
2411 static const WCHAR szOutOfNoRbDiskSpace[] =
2412 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2413 MSICOMPONENT *comp;
2414 MSIQUERY *view;
2415 WCHAR *level, *primary_key, *primary_folder;
2416 UINT rc;
2418 TRACE("Building directory properties\n");
2419 msi_resolve_target_folder( package, szTargetDir, TRUE );
2421 TRACE("Evaluating component conditions\n");
2422 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2424 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2426 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2427 comp->Enabled = FALSE;
2429 else
2430 comp->Enabled = TRUE;
2432 get_client_counts( package );
2434 /* read components states from the registry */
2435 ACTION_GetComponentInstallStates(package);
2436 ACTION_GetFeatureInstallStates(package);
2438 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2440 TRACE("Evaluating feature conditions\n");
2442 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2443 if (rc == ERROR_SUCCESS)
2445 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2446 msiobj_release( &view->hdr );
2447 if (rc != ERROR_SUCCESS)
2448 return rc;
2452 TRACE("Calculating file cost\n");
2453 calculate_file_cost( package );
2455 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2456 /* set default run level if not set */
2457 level = msi_dup_property( package->db, szInstallLevel );
2458 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2459 msi_free(level);
2461 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2463 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2465 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2467 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2468 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2470 static const WCHAR fmtW[] = {'%','l','u',0};
2471 ULARGE_INTEGER free;
2472 ULONGLONG required;
2473 WCHAR buf[21];
2475 primary_folder[2] = 0;
2476 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2478 sprintfW( buf, fmtW, free.QuadPart / 512 );
2479 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2481 required = get_volume_space_required( package );
2482 sprintfW( buf, fmtW, required / 512 );
2483 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2485 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2486 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2487 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2489 msi_free( primary_folder );
2491 msi_free( primary_key );
2494 /* FIXME: check volume disk space */
2495 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2496 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2498 return ERROR_SUCCESS;
2501 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2503 BYTE *data;
2505 if (!value)
2507 *size = sizeof(WCHAR);
2508 *type = REG_SZ;
2509 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2510 return data;
2512 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2514 if (value[1]=='x')
2516 LPWSTR ptr;
2517 CHAR byte[5];
2518 LPWSTR deformated = NULL;
2519 int count;
2521 deformat_string(package, &value[2], &deformated);
2523 /* binary value type */
2524 ptr = deformated;
2525 *type = REG_BINARY;
2526 if (strlenW(ptr)%2)
2527 *size = (strlenW(ptr)/2)+1;
2528 else
2529 *size = strlenW(ptr)/2;
2531 data = msi_alloc(*size);
2533 byte[0] = '0';
2534 byte[1] = 'x';
2535 byte[4] = 0;
2536 count = 0;
2537 /* if uneven pad with a zero in front */
2538 if (strlenW(ptr)%2)
2540 byte[2]= '0';
2541 byte[3]= *ptr;
2542 ptr++;
2543 data[count] = (BYTE)strtol(byte,NULL,0);
2544 count ++;
2545 TRACE("Uneven byte count\n");
2547 while (*ptr)
2549 byte[2]= *ptr;
2550 ptr++;
2551 byte[3]= *ptr;
2552 ptr++;
2553 data[count] = (BYTE)strtol(byte,NULL,0);
2554 count ++;
2556 msi_free(deformated);
2558 TRACE("Data %i bytes(%i)\n",*size,count);
2560 else
2562 LPWSTR deformated;
2563 LPWSTR p;
2564 DWORD d = 0;
2565 deformat_string(package, &value[1], &deformated);
2567 *type=REG_DWORD;
2568 *size = sizeof(DWORD);
2569 data = msi_alloc(*size);
2570 p = deformated;
2571 if (*p == '-')
2572 p++;
2573 while (*p)
2575 if ( (*p < '0') || (*p > '9') )
2576 break;
2577 d *= 10;
2578 d += (*p - '0');
2579 p++;
2581 if (deformated[0] == '-')
2582 d = -d;
2583 *(LPDWORD)data = d;
2584 TRACE("DWORD %i\n",*(LPDWORD)data);
2586 msi_free(deformated);
2589 else
2591 const WCHAR *ptr = value;
2593 *type = REG_SZ;
2594 if (value[0] == '#')
2596 ptr++; len--;
2597 if (value[1] == '%')
2599 ptr++; len--;
2600 *type = REG_EXPAND_SZ;
2603 data = (BYTE *)msi_strdupW( ptr, len );
2604 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2605 *size = (len + 1) * sizeof(WCHAR);
2607 return data;
2610 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2612 const WCHAR *ret;
2614 switch (root)
2616 case -1:
2617 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2619 *root_key = HKEY_LOCAL_MACHINE;
2620 ret = szHLM;
2622 else
2624 *root_key = HKEY_CURRENT_USER;
2625 ret = szHCU;
2627 break;
2628 case 0:
2629 *root_key = HKEY_CLASSES_ROOT;
2630 ret = szHCR;
2631 break;
2632 case 1:
2633 *root_key = HKEY_CURRENT_USER;
2634 ret = szHCU;
2635 break;
2636 case 2:
2637 *root_key = HKEY_LOCAL_MACHINE;
2638 ret = szHLM;
2639 break;
2640 case 3:
2641 *root_key = HKEY_USERS;
2642 ret = szHU;
2643 break;
2644 default:
2645 ERR("Unknown root %i\n", root);
2646 return NULL;
2649 return ret;
2652 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2654 REGSAM view = 0;
2655 if (is_wow64 || is_64bit)
2656 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2657 return view;
2660 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2662 WCHAR *subkey, *p, *q;
2663 HKEY hkey, ret = NULL;
2664 LONG res;
2666 access |= get_registry_view( comp );
2668 if (!(subkey = strdupW( path ))) return NULL;
2669 p = subkey;
2670 if ((q = strchrW( p, '\\' ))) *q = 0;
2671 if (create)
2672 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2673 else
2674 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2675 if (res)
2677 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2678 msi_free( subkey );
2679 return NULL;
2681 if (q && q[1])
2683 ret = open_key( comp, hkey, q + 1, create, access );
2684 RegCloseKey( hkey );
2686 else ret = hkey;
2687 msi_free( subkey );
2688 return ret;
2691 static BOOL is_special_entry( const WCHAR *name )
2693 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2696 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2698 const WCHAR *p = str;
2699 WCHAR **ret;
2700 int i = 0;
2702 *count = 0;
2703 if (!str) return NULL;
2704 while ((p - str) < len)
2706 p += strlenW( p ) + 1;
2707 (*count)++;
2709 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2710 p = str;
2711 while ((p - str) < len)
2713 if (!(ret[i] = strdupW( p )))
2715 for (; i >= 0; i--) msi_free( ret[i] );
2716 msi_free( ret );
2717 return NULL;
2719 p += strlenW( p ) + 1;
2720 i++;
2722 return ret;
2725 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2726 WCHAR **right, DWORD right_count, DWORD *size )
2728 WCHAR *ret, *p;
2729 unsigned int i;
2731 *size = sizeof(WCHAR);
2732 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2733 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2735 if (!(ret = p = msi_alloc( *size ))) return NULL;
2737 for (i = 0; i < left_count; i++)
2739 strcpyW( p, left[i] );
2740 p += strlenW( p ) + 1;
2742 for (i = 0; i < right_count; i++)
2744 strcpyW( p, right[i] );
2745 p += strlenW( p ) + 1;
2747 *p = 0;
2748 return ret;
2751 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2752 WCHAR **new, DWORD new_count )
2754 DWORD ret = old_count;
2755 unsigned int i, j, k;
2757 for (i = 0; i < new_count; i++)
2759 for (j = 0; j < old_count; j++)
2761 if (old[j] && !strcmpW( new[i], old[j] ))
2763 msi_free( old[j] );
2764 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2765 old[k] = NULL;
2766 ret--;
2770 return ret;
2773 enum join_op
2775 JOIN_OP_APPEND,
2776 JOIN_OP_PREPEND,
2777 JOIN_OP_REPLACE
2780 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2781 WCHAR **new, DWORD new_count, DWORD *size )
2783 switch (op)
2785 case JOIN_OP_APPEND:
2786 old_count = remove_duplicate_values( old, old_count, new, new_count );
2787 return flatten_multi_string_values( old, old_count, new, new_count, size );
2789 case JOIN_OP_PREPEND:
2790 old_count = remove_duplicate_values( old, old_count, new, new_count );
2791 return flatten_multi_string_values( new, new_count, old, old_count, size );
2793 case JOIN_OP_REPLACE:
2794 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2796 default:
2797 ERR("unhandled join op %u\n", op);
2798 return NULL;
2802 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2803 BYTE *new_value, DWORD new_size, DWORD *size )
2805 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2806 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2807 enum join_op op = JOIN_OP_REPLACE;
2808 WCHAR **old = NULL, **new = NULL;
2809 BYTE *ret;
2811 if (new_size / sizeof(WCHAR) - 1 > 1)
2813 new_ptr = (const WCHAR *)new_value;
2814 new_len = new_size / sizeof(WCHAR) - 1;
2816 if (!new_ptr[0] && new_ptr[new_len - 1])
2818 op = JOIN_OP_APPEND;
2819 new_len--;
2820 new_ptr++;
2822 else if (new_ptr[0] && !new_ptr[new_len - 1])
2824 op = JOIN_OP_PREPEND;
2825 new_len--;
2827 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2829 op = JOIN_OP_REPLACE;
2830 new_len -= 2;
2831 new_ptr++;
2833 new = split_multi_string_values( new_ptr, new_len, &new_count );
2835 if (old_size / sizeof(WCHAR) - 1 > 1)
2837 old_ptr = (const WCHAR *)old_value;
2838 old_len = old_size / sizeof(WCHAR) - 1;
2839 old = split_multi_string_values( old_ptr, old_len, &old_count );
2841 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2842 for (i = 0; i < old_count; i++) msi_free( old[i] );
2843 for (i = 0; i < new_count; i++) msi_free( new[i] );
2844 msi_free( old );
2845 msi_free( new );
2846 return ret;
2849 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2851 BYTE *ret;
2852 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2853 if (!(ret = msi_alloc( *size ))) return NULL;
2854 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2855 return ret;
2858 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2860 MSIPACKAGE *package = param;
2861 BYTE *new_value, *old_value = NULL;
2862 HKEY root_key, hkey;
2863 DWORD type, old_type, new_size, old_size = 0;
2864 LPWSTR deformated, uikey;
2865 const WCHAR *szRoot, *component, *name, *key, *str;
2866 MSICOMPONENT *comp;
2867 MSIRECORD * uirow;
2868 INT root;
2869 BOOL check_first = FALSE;
2870 int len;
2872 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2874 component = MSI_RecordGetString(row, 6);
2875 comp = msi_get_loaded_component(package,component);
2876 if (!comp)
2877 return ERROR_SUCCESS;
2879 comp->Action = msi_get_component_action( package, comp );
2880 if (comp->Action != INSTALLSTATE_LOCAL)
2882 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2883 return ERROR_SUCCESS;
2886 name = MSI_RecordGetString(row, 4);
2887 if( MSI_RecordIsNull(row,5) && name )
2889 /* null values can have special meanings */
2890 if (name[0]=='-' && name[1] == 0)
2891 return ERROR_SUCCESS;
2892 if ((name[0] == '+' || name[0] == '*') && !name[1])
2893 check_first = TRUE;
2896 root = MSI_RecordGetInteger(row,2);
2897 key = MSI_RecordGetString(row, 3);
2899 szRoot = get_root_key( package, root, &root_key );
2900 if (!szRoot)
2901 return ERROR_SUCCESS;
2903 deformat_string(package, key , &deformated);
2904 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2905 strcpyW(uikey,szRoot);
2906 strcatW(uikey,deformated);
2908 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2910 ERR("Could not create key %s\n", debugstr_w(deformated));
2911 msi_free(uikey);
2912 msi_free(deformated);
2913 return ERROR_FUNCTION_FAILED;
2915 msi_free( deformated );
2916 str = msi_record_get_string( row, 5, NULL );
2917 len = deformat_string( package, str, &deformated );
2918 new_value = parse_value( package, deformated, len, &type, &new_size );
2920 msi_free( deformated );
2921 deformat_string(package, name, &deformated);
2923 if (!is_special_entry( name ))
2925 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2926 if (type == REG_MULTI_SZ)
2928 BYTE *new;
2929 if (old_value && old_type != REG_MULTI_SZ)
2931 msi_free( old_value );
2932 old_value = NULL;
2933 old_size = 0;
2935 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2936 msi_free( new_value );
2937 new_value = new;
2939 if (!check_first)
2941 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2942 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2944 else if (!old_value)
2946 if (deformated || new_size)
2948 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2949 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2952 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2954 RegCloseKey(hkey);
2956 uirow = MSI_CreateRecord(3);
2957 MSI_RecordSetStringW(uirow,2,deformated);
2958 MSI_RecordSetStringW(uirow,1,uikey);
2959 if (type == REG_SZ || type == REG_EXPAND_SZ)
2960 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2961 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2962 msiobj_release( &uirow->hdr );
2964 msi_free(new_value);
2965 msi_free(old_value);
2966 msi_free(deformated);
2967 msi_free(uikey);
2969 return ERROR_SUCCESS;
2972 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2974 static const WCHAR query[] = {
2975 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2976 '`','R','e','g','i','s','t','r','y','`',0};
2977 MSIQUERY *view;
2978 UINT rc;
2980 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2981 if (rc != ERROR_SUCCESS)
2982 return ERROR_SUCCESS;
2984 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2985 msiobj_release(&view->hdr);
2986 return rc;
2989 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2991 REGSAM access = 0;
2992 WCHAR *subkey, *p;
2993 HKEY hkey;
2994 LONG res;
2996 access |= get_registry_view( comp );
2998 if (!(subkey = strdupW( path ))) return;
3001 if ((p = strrchrW( subkey, '\\' )))
3003 *p = 0;
3004 if (!p[1]) continue; /* trailing backslash */
3005 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3006 if (!hkey) break;
3007 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3008 RegCloseKey( hkey );
3010 else
3011 res = RegDeleteKeyExW( root, subkey, access, 0 );
3012 if (res)
3014 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3015 break;
3017 } while (p);
3018 msi_free( subkey );
3021 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3023 LONG res;
3024 HKEY hkey;
3025 DWORD num_subkeys, num_values;
3027 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3029 if ((res = RegDeleteValueW( hkey, value )))
3030 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3032 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3033 NULL, NULL, NULL, NULL );
3034 RegCloseKey( hkey );
3035 if (!res && !num_subkeys && !num_values)
3037 TRACE("removing empty key %s\n", debugstr_w(path));
3038 delete_key( comp, root, path );
3043 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3045 LONG res;
3046 HKEY hkey;
3048 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3049 res = RegDeleteTreeW( hkey, NULL );
3050 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3051 delete_key( comp, root, path );
3052 RegCloseKey( hkey );
3055 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3057 MSIPACKAGE *package = param;
3058 LPCWSTR component, name, key_str, root_key_str;
3059 LPWSTR deformated_key, deformated_name, ui_key_str;
3060 MSICOMPONENT *comp;
3061 MSIRECORD *uirow;
3062 BOOL delete_key = FALSE;
3063 HKEY hkey_root;
3064 UINT size;
3065 INT root;
3067 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3069 component = MSI_RecordGetString( row, 6 );
3070 comp = msi_get_loaded_component( package, component );
3071 if (!comp)
3072 return ERROR_SUCCESS;
3074 comp->Action = msi_get_component_action( package, comp );
3075 if (comp->Action != INSTALLSTATE_ABSENT)
3077 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3078 return ERROR_SUCCESS;
3081 name = MSI_RecordGetString( row, 4 );
3082 if (MSI_RecordIsNull( row, 5 ) && name )
3084 if (name[0] == '+' && !name[1])
3085 return ERROR_SUCCESS;
3086 if ((name[0] == '-' || name[0] == '*') && !name[1])
3088 delete_key = TRUE;
3089 name = NULL;
3093 root = MSI_RecordGetInteger( row, 2 );
3094 key_str = MSI_RecordGetString( row, 3 );
3096 root_key_str = get_root_key( package, root, &hkey_root );
3097 if (!root_key_str)
3098 return ERROR_SUCCESS;
3100 deformat_string( package, key_str, &deformated_key );
3101 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3102 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3103 strcpyW( ui_key_str, root_key_str );
3104 strcatW( ui_key_str, deformated_key );
3106 deformat_string( package, name, &deformated_name );
3108 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3109 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3110 msi_free( deformated_key );
3112 uirow = MSI_CreateRecord( 2 );
3113 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3114 MSI_RecordSetStringW( uirow, 2, deformated_name );
3115 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3116 msiobj_release( &uirow->hdr );
3118 msi_free( ui_key_str );
3119 msi_free( deformated_name );
3120 return ERROR_SUCCESS;
3123 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3125 MSIPACKAGE *package = param;
3126 LPCWSTR component, name, key_str, root_key_str;
3127 LPWSTR deformated_key, deformated_name, ui_key_str;
3128 MSICOMPONENT *comp;
3129 MSIRECORD *uirow;
3130 BOOL delete_key = FALSE;
3131 HKEY hkey_root;
3132 UINT size;
3133 INT root;
3135 component = MSI_RecordGetString( row, 5 );
3136 comp = msi_get_loaded_component( package, component );
3137 if (!comp)
3138 return ERROR_SUCCESS;
3140 comp->Action = msi_get_component_action( package, comp );
3141 if (comp->Action != INSTALLSTATE_LOCAL)
3143 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3144 return ERROR_SUCCESS;
3147 if ((name = MSI_RecordGetString( row, 4 )))
3149 if (name[0] == '-' && !name[1])
3151 delete_key = TRUE;
3152 name = NULL;
3156 root = MSI_RecordGetInteger( row, 2 );
3157 key_str = MSI_RecordGetString( row, 3 );
3159 root_key_str = get_root_key( package, root, &hkey_root );
3160 if (!root_key_str)
3161 return ERROR_SUCCESS;
3163 deformat_string( package, key_str, &deformated_key );
3164 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3165 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3166 strcpyW( ui_key_str, root_key_str );
3167 strcatW( ui_key_str, deformated_key );
3169 deformat_string( package, name, &deformated_name );
3171 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3172 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3173 msi_free( deformated_key );
3175 uirow = MSI_CreateRecord( 2 );
3176 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3177 MSI_RecordSetStringW( uirow, 2, deformated_name );
3178 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3179 msiobj_release( &uirow->hdr );
3181 msi_free( ui_key_str );
3182 msi_free( deformated_name );
3183 return ERROR_SUCCESS;
3186 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3188 static const WCHAR registry_query[] = {
3189 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3190 '`','R','e','g','i','s','t','r','y','`',0};
3191 static const WCHAR remove_registry_query[] = {
3192 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3193 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3194 MSIQUERY *view;
3195 UINT rc;
3197 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3198 if (rc == ERROR_SUCCESS)
3200 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3201 msiobj_release( &view->hdr );
3202 if (rc != ERROR_SUCCESS)
3203 return rc;
3205 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3206 if (rc == ERROR_SUCCESS)
3208 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3209 msiobj_release( &view->hdr );
3210 if (rc != ERROR_SUCCESS)
3211 return rc;
3213 return ERROR_SUCCESS;
3216 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3218 return ERROR_SUCCESS;
3222 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3224 static const WCHAR query[]= {
3225 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3226 '`','R','e','g','i','s','t','r','y','`',0};
3227 MSICOMPONENT *comp;
3228 DWORD total = 0, count = 0;
3229 MSIQUERY *view;
3230 MSIFEATURE *feature;
3231 MSIFILE *file;
3232 UINT rc;
3234 TRACE("InstallValidate\n");
3236 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3237 if (rc == ERROR_SUCCESS)
3239 rc = MSI_IterateRecords( view, &count, NULL, package );
3240 msiobj_release( &view->hdr );
3241 if (rc != ERROR_SUCCESS)
3242 return rc;
3243 total += count * REG_PROGRESS_VALUE;
3245 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3246 total += COMPONENT_PROGRESS_VALUE;
3248 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3249 total += file->FileSize;
3251 msi_ui_progress( package, 0, total, 0, 0 );
3253 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3255 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3256 debugstr_w(feature->Feature), feature->Installed,
3257 feature->ActionRequest, feature->Action);
3259 return ERROR_SUCCESS;
3262 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3264 MSIPACKAGE* package = param;
3265 LPCWSTR cond = NULL;
3266 LPCWSTR message = NULL;
3267 UINT r;
3269 static const WCHAR title[]=
3270 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3272 cond = MSI_RecordGetString(row,1);
3274 r = MSI_EvaluateConditionW(package,cond);
3275 if (r == MSICONDITION_FALSE)
3277 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3279 LPWSTR deformated;
3280 message = MSI_RecordGetString(row,2);
3281 deformat_string(package,message,&deformated);
3282 MessageBoxW(NULL,deformated,title,MB_OK);
3283 msi_free(deformated);
3286 return ERROR_INSTALL_FAILURE;
3289 return ERROR_SUCCESS;
3292 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3294 static const WCHAR query[] = {
3295 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3296 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3297 MSIQUERY *view;
3298 UINT rc;
3300 TRACE("Checking launch conditions\n");
3302 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3303 if (rc != ERROR_SUCCESS)
3304 return ERROR_SUCCESS;
3306 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3307 msiobj_release(&view->hdr);
3308 return rc;
3311 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3314 if (!cmp->KeyPath)
3315 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3317 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3319 static const WCHAR query[] = {
3320 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3321 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3322 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3323 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3324 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3325 MSIRECORD *row;
3326 UINT root, len;
3327 LPWSTR deformated, buffer, deformated_name;
3328 LPCWSTR key, name;
3330 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3331 if (!row)
3332 return NULL;
3334 root = MSI_RecordGetInteger(row,2);
3335 key = MSI_RecordGetString(row, 3);
3336 name = MSI_RecordGetString(row, 4);
3337 deformat_string(package, key , &deformated);
3338 deformat_string(package, name, &deformated_name);
3340 len = strlenW(deformated) + 6;
3341 if (deformated_name)
3342 len+=strlenW(deformated_name);
3344 buffer = msi_alloc( len *sizeof(WCHAR));
3346 if (deformated_name)
3347 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3348 else
3349 sprintfW(buffer,fmt,root,deformated);
3351 msi_free(deformated);
3352 msi_free(deformated_name);
3353 msiobj_release(&row->hdr);
3355 return buffer;
3357 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3359 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3360 return NULL;
3362 else
3364 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3366 if (file)
3367 return strdupW( file->TargetPath );
3369 return NULL;
3372 static HKEY openSharedDLLsKey(void)
3374 HKEY hkey=0;
3375 static const WCHAR path[] =
3376 {'S','o','f','t','w','a','r','e','\\',
3377 'M','i','c','r','o','s','o','f','t','\\',
3378 'W','i','n','d','o','w','s','\\',
3379 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3380 'S','h','a','r','e','d','D','L','L','s',0};
3382 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3383 return hkey;
3386 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3388 HKEY hkey;
3389 DWORD count=0;
3390 DWORD type;
3391 DWORD sz = sizeof(count);
3392 DWORD rc;
3394 hkey = openSharedDLLsKey();
3395 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3396 if (rc != ERROR_SUCCESS)
3397 count = 0;
3398 RegCloseKey(hkey);
3399 return count;
3402 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3404 HKEY hkey;
3406 hkey = openSharedDLLsKey();
3407 if (count > 0)
3408 msi_reg_set_val_dword( hkey, path, count );
3409 else
3410 RegDeleteValueW(hkey,path);
3411 RegCloseKey(hkey);
3412 return count;
3415 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3417 MSIFEATURE *feature;
3418 INT count = 0;
3419 BOOL write = FALSE;
3421 /* only refcount DLLs */
3422 if (comp->KeyPath == NULL ||
3423 comp->assembly ||
3424 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3425 comp->Attributes & msidbComponentAttributesODBCDataSource)
3426 write = FALSE;
3427 else
3429 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3430 write = (count > 0);
3432 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3433 write = TRUE;
3436 /* increment counts */
3437 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3439 ComponentList *cl;
3441 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3442 continue;
3444 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3446 if ( cl->component == comp )
3447 count++;
3451 /* decrement counts */
3452 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3454 ComponentList *cl;
3456 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3457 continue;
3459 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3461 if ( cl->component == comp )
3462 count--;
3466 /* ref count all the files in the component */
3467 if (write)
3469 MSIFILE *file;
3471 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3473 if (file->Component == comp)
3474 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3478 /* add a count for permanent */
3479 if (comp->Attributes & msidbComponentAttributesPermanent)
3480 count ++;
3482 comp->RefCount = count;
3484 if (write)
3485 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3488 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3490 if (comp->assembly)
3492 const WCHAR prefixW[] = {'<','\\',0};
3493 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3494 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3496 if (keypath)
3498 strcpyW( keypath, prefixW );
3499 strcatW( keypath, comp->assembly->display_name );
3501 return keypath;
3503 return resolve_keypath( package, comp );
3506 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3508 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3509 UINT rc;
3510 MSICOMPONENT *comp;
3511 HKEY hkey;
3513 TRACE("\n");
3515 squash_guid( package->ProductCode, squashed_pc );
3516 msi_set_sourcedir_props(package, FALSE);
3518 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3520 MSIRECORD *uirow;
3521 INSTALLSTATE action;
3523 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3524 if (!comp->ComponentId)
3525 continue;
3527 squash_guid( comp->ComponentId, squashed_cc );
3528 msi_free( comp->FullKeypath );
3529 comp->FullKeypath = build_full_keypath( package, comp );
3531 ACTION_RefCountComponent( package, comp );
3533 if (package->need_rollback) action = comp->Installed;
3534 else action = comp->ActionRequest;
3536 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3537 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3538 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3540 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3542 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3543 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3544 else
3545 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3547 if (rc != ERROR_SUCCESS)
3548 continue;
3550 if (comp->Attributes & msidbComponentAttributesPermanent)
3552 static const WCHAR szPermKey[] =
3553 { '0','0','0','0','0','0','0','0','0','0','0','0',
3554 '0','0','0','0','0','0','0','0','0','0','0','0',
3555 '0','0','0','0','0','0','0','0',0 };
3557 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3559 if (action == INSTALLSTATE_LOCAL)
3560 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3561 else
3563 MSIFILE *file;
3564 MSIRECORD *row;
3565 LPWSTR ptr, ptr2;
3566 WCHAR source[MAX_PATH];
3567 WCHAR base[MAX_PATH];
3568 LPWSTR sourcepath;
3570 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3571 static const WCHAR query[] = {
3572 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3573 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3574 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3575 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3576 '`','D','i','s','k','I','d','`',0};
3578 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3579 continue;
3581 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3582 return ERROR_FUNCTION_FAILED;
3584 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3585 ptr2 = strrchrW(source, '\\') + 1;
3586 msiobj_release(&row->hdr);
3588 lstrcpyW(base, package->PackagePath);
3589 ptr = strrchrW(base, '\\');
3590 *(ptr + 1) = '\0';
3592 sourcepath = msi_resolve_file_source(package, file);
3593 ptr = sourcepath + lstrlenW(base);
3594 lstrcpyW(ptr2, ptr);
3595 msi_free(sourcepath);
3597 msi_reg_set_val_str( hkey, squashed_pc, source );
3599 RegCloseKey(hkey);
3601 else if (action == INSTALLSTATE_ABSENT)
3603 if (comp->num_clients <= 0)
3605 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3606 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3607 else
3608 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3610 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3612 else
3614 LONG res;
3616 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3617 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3618 else
3619 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3621 if (rc != ERROR_SUCCESS)
3623 WARN( "failed to open component key %u\n", rc );
3624 continue;
3626 res = RegDeleteValueW( hkey, squashed_pc );
3627 RegCloseKey(hkey);
3628 if (res) WARN( "failed to delete component value %d\n", res );
3632 /* UI stuff */
3633 uirow = MSI_CreateRecord(3);
3634 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3635 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3636 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3637 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3638 msiobj_release( &uirow->hdr );
3640 return ERROR_SUCCESS;
3643 typedef struct {
3644 CLSID clsid;
3645 LPWSTR source;
3647 LPWSTR path;
3648 ITypeLib *ptLib;
3649 } typelib_struct;
3651 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3652 LPWSTR lpszName, LONG_PTR lParam)
3654 TLIBATTR *attr;
3655 typelib_struct *tl_struct = (typelib_struct*) lParam;
3656 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3657 int sz;
3658 HRESULT res;
3660 if (!IS_INTRESOURCE(lpszName))
3662 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3663 return TRUE;
3666 sz = strlenW(tl_struct->source)+4;
3667 sz *= sizeof(WCHAR);
3669 if ((INT_PTR)lpszName == 1)
3670 tl_struct->path = strdupW(tl_struct->source);
3671 else
3673 tl_struct->path = msi_alloc(sz);
3674 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3677 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3678 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3679 if (FAILED(res))
3681 msi_free(tl_struct->path);
3682 tl_struct->path = NULL;
3684 return TRUE;
3687 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3688 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3690 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3691 return FALSE;
3694 msi_free(tl_struct->path);
3695 tl_struct->path = NULL;
3697 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3698 ITypeLib_Release(tl_struct->ptLib);
3700 return TRUE;
3703 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3705 MSIPACKAGE* package = param;
3706 LPCWSTR component;
3707 MSICOMPONENT *comp;
3708 MSIFILE *file;
3709 typelib_struct tl_struct;
3710 ITypeLib *tlib;
3711 HMODULE module;
3712 HRESULT hr;
3714 component = MSI_RecordGetString(row,3);
3715 comp = msi_get_loaded_component(package,component);
3716 if (!comp)
3717 return ERROR_SUCCESS;
3719 comp->Action = msi_get_component_action( package, comp );
3720 if (comp->Action != INSTALLSTATE_LOCAL)
3722 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3723 return ERROR_SUCCESS;
3726 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3728 TRACE("component has no key path\n");
3729 return ERROR_SUCCESS;
3731 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3733 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3734 if (module)
3736 LPCWSTR guid;
3737 guid = MSI_RecordGetString(row,1);
3738 CLSIDFromString( guid, &tl_struct.clsid);
3739 tl_struct.source = strdupW( file->TargetPath );
3740 tl_struct.path = NULL;
3742 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3743 (LONG_PTR)&tl_struct);
3745 if (tl_struct.path)
3747 LPCWSTR helpid, help_path = NULL;
3748 HRESULT res;
3750 helpid = MSI_RecordGetString(row,6);
3752 if (helpid) help_path = msi_get_target_folder( package, helpid );
3753 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3755 if (FAILED(res))
3756 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3757 else
3758 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3760 ITypeLib_Release(tl_struct.ptLib);
3761 msi_free(tl_struct.path);
3763 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3765 FreeLibrary(module);
3766 msi_free(tl_struct.source);
3768 else
3770 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3771 if (FAILED(hr))
3773 ERR("Failed to load type library: %08x\n", hr);
3774 return ERROR_INSTALL_FAILURE;
3777 ITypeLib_Release(tlib);
3780 return ERROR_SUCCESS;
3783 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3785 static const WCHAR query[] = {
3786 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3787 '`','T','y','p','e','L','i','b','`',0};
3788 MSIQUERY *view;
3789 UINT rc;
3791 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3792 if (rc != ERROR_SUCCESS)
3793 return ERROR_SUCCESS;
3795 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3796 msiobj_release(&view->hdr);
3797 return rc;
3800 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3802 MSIPACKAGE *package = param;
3803 LPCWSTR component, guid;
3804 MSICOMPONENT *comp;
3805 GUID libid;
3806 UINT version;
3807 LCID language;
3808 SYSKIND syskind;
3809 HRESULT hr;
3811 component = MSI_RecordGetString( row, 3 );
3812 comp = msi_get_loaded_component( package, component );
3813 if (!comp)
3814 return ERROR_SUCCESS;
3816 comp->Action = msi_get_component_action( package, comp );
3817 if (comp->Action != INSTALLSTATE_ABSENT)
3819 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3820 return ERROR_SUCCESS;
3822 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3824 guid = MSI_RecordGetString( row, 1 );
3825 CLSIDFromString( guid, &libid );
3826 version = MSI_RecordGetInteger( row, 4 );
3827 language = MSI_RecordGetInteger( row, 2 );
3829 #ifdef _WIN64
3830 syskind = SYS_WIN64;
3831 #else
3832 syskind = SYS_WIN32;
3833 #endif
3835 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3836 if (FAILED(hr))
3838 WARN("Failed to unregister typelib: %08x\n", hr);
3841 return ERROR_SUCCESS;
3844 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3846 static const WCHAR query[] = {
3847 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3848 '`','T','y','p','e','L','i','b','`',0};
3849 MSIQUERY *view;
3850 UINT rc;
3852 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3853 if (rc != ERROR_SUCCESS)
3854 return ERROR_SUCCESS;
3856 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3857 msiobj_release( &view->hdr );
3858 return rc;
3861 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3863 static const WCHAR szlnk[] = {'.','l','n','k',0};
3864 LPCWSTR directory, extension, link_folder;
3865 LPWSTR link_file, filename;
3867 directory = MSI_RecordGetString( row, 2 );
3868 link_folder = msi_get_target_folder( package, directory );
3869 if (!link_folder)
3871 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3872 return NULL;
3874 /* may be needed because of a bug somewhere else */
3875 msi_create_full_path( link_folder );
3877 filename = msi_dup_record_field( row, 3 );
3878 msi_reduce_to_long_filename( filename );
3880 extension = strrchrW( filename, '.' );
3881 if (!extension || strcmpiW( extension, szlnk ))
3883 int len = strlenW( filename );
3884 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3885 memcpy( filename + len, szlnk, sizeof(szlnk) );
3887 link_file = msi_build_directory_name( 2, link_folder, filename );
3888 msi_free( filename );
3890 return link_file;
3893 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3895 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3896 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3897 WCHAR *folder, *dest, *path;
3899 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3900 folder = msi_dup_property( package->db, szWindowsFolder );
3901 else
3903 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3904 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3905 msi_free( appdata );
3907 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3908 msi_create_full_path( dest );
3909 path = msi_build_directory_name( 2, dest, icon_name );
3910 msi_free( folder );
3911 msi_free( dest );
3912 return path;
3915 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3917 MSIPACKAGE *package = param;
3918 LPWSTR link_file, deformated, path;
3919 LPCWSTR component, target;
3920 MSICOMPONENT *comp;
3921 IShellLinkW *sl = NULL;
3922 IPersistFile *pf = NULL;
3923 HRESULT res;
3925 component = MSI_RecordGetString(row, 4);
3926 comp = msi_get_loaded_component(package, component);
3927 if (!comp)
3928 return ERROR_SUCCESS;
3930 comp->Action = msi_get_component_action( package, comp );
3931 if (comp->Action != INSTALLSTATE_LOCAL)
3933 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3934 return ERROR_SUCCESS;
3936 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3938 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3939 &IID_IShellLinkW, (LPVOID *) &sl );
3941 if (FAILED( res ))
3943 ERR("CLSID_ShellLink not available\n");
3944 goto err;
3947 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3948 if (FAILED( res ))
3950 ERR("QueryInterface(IID_IPersistFile) failed\n");
3951 goto err;
3954 target = MSI_RecordGetString(row, 5);
3955 if (strchrW(target, '['))
3957 deformat_string( package, target, &path );
3958 TRACE("target path is %s\n", debugstr_w(path));
3959 IShellLinkW_SetPath( sl, path );
3960 msi_free( path );
3962 else
3964 FIXME("poorly handled shortcut format, advertised shortcut\n");
3965 path = resolve_keypath( package, comp );
3966 IShellLinkW_SetPath( sl, path );
3967 msi_free( path );
3970 if (!MSI_RecordIsNull(row,6))
3972 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3973 deformat_string(package, arguments, &deformated);
3974 IShellLinkW_SetArguments(sl,deformated);
3975 msi_free(deformated);
3978 if (!MSI_RecordIsNull(row,7))
3980 LPCWSTR description = MSI_RecordGetString(row, 7);
3981 IShellLinkW_SetDescription(sl, description);
3984 if (!MSI_RecordIsNull(row,8))
3985 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3987 if (!MSI_RecordIsNull(row,9))
3989 INT index;
3990 LPCWSTR icon = MSI_RecordGetString(row, 9);
3992 path = msi_build_icon_path(package, icon);
3993 index = MSI_RecordGetInteger(row,10);
3995 /* no value means 0 */
3996 if (index == MSI_NULL_INTEGER)
3997 index = 0;
3999 IShellLinkW_SetIconLocation(sl, path, index);
4000 msi_free(path);
4003 if (!MSI_RecordIsNull(row,11))
4004 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4006 if (!MSI_RecordIsNull(row,12))
4008 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4009 full_path = msi_get_target_folder( package, wkdir );
4010 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4012 link_file = get_link_file(package, row);
4014 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4015 IPersistFile_Save(pf, link_file, FALSE);
4016 msi_free(link_file);
4018 err:
4019 if (pf)
4020 IPersistFile_Release( pf );
4021 if (sl)
4022 IShellLinkW_Release( sl );
4024 return ERROR_SUCCESS;
4027 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4029 static const WCHAR query[] = {
4030 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4031 '`','S','h','o','r','t','c','u','t','`',0};
4032 MSIQUERY *view;
4033 HRESULT res;
4034 UINT rc;
4036 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4037 if (rc != ERROR_SUCCESS)
4038 return ERROR_SUCCESS;
4040 res = CoInitialize( NULL );
4042 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4043 msiobj_release(&view->hdr);
4045 if (SUCCEEDED(res)) CoUninitialize();
4046 return rc;
4049 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4051 MSIPACKAGE *package = param;
4052 LPWSTR link_file;
4053 LPCWSTR component;
4054 MSICOMPONENT *comp;
4056 component = MSI_RecordGetString( row, 4 );
4057 comp = msi_get_loaded_component( package, component );
4058 if (!comp)
4059 return ERROR_SUCCESS;
4061 comp->Action = msi_get_component_action( package, comp );
4062 if (comp->Action != INSTALLSTATE_ABSENT)
4064 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4065 return ERROR_SUCCESS;
4067 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4069 link_file = get_link_file( package, row );
4071 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4072 if (!DeleteFileW( link_file ))
4074 WARN("Failed to remove shortcut file %u\n", GetLastError());
4076 msi_free( link_file );
4078 return ERROR_SUCCESS;
4081 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4083 static const WCHAR query[] = {
4084 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4085 '`','S','h','o','r','t','c','u','t','`',0};
4086 MSIQUERY *view;
4087 UINT rc;
4089 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4090 if (rc != ERROR_SUCCESS)
4091 return ERROR_SUCCESS;
4093 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4094 msiobj_release( &view->hdr );
4095 return rc;
4098 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4100 MSIPACKAGE* package = param;
4101 HANDLE the_file;
4102 LPWSTR FilePath;
4103 LPCWSTR FileName;
4104 CHAR buffer[1024];
4105 DWORD sz;
4106 UINT rc;
4108 FileName = MSI_RecordGetString(row,1);
4109 if (!FileName)
4111 ERR("Unable to get FileName\n");
4112 return ERROR_SUCCESS;
4115 FilePath = msi_build_icon_path(package, FileName);
4117 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4119 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4120 FILE_ATTRIBUTE_NORMAL, NULL);
4122 if (the_file == INVALID_HANDLE_VALUE)
4124 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4125 msi_free(FilePath);
4126 return ERROR_SUCCESS;
4131 DWORD write;
4132 sz = 1024;
4133 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4134 if (rc != ERROR_SUCCESS)
4136 ERR("Failed to get stream\n");
4137 DeleteFileW(FilePath);
4138 break;
4140 WriteFile(the_file,buffer,sz,&write,NULL);
4141 } while (sz == 1024);
4143 msi_free(FilePath);
4144 CloseHandle(the_file);
4146 return ERROR_SUCCESS;
4149 static UINT msi_publish_icons(MSIPACKAGE *package)
4151 static const WCHAR query[]= {
4152 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4153 '`','I','c','o','n','`',0};
4154 MSIQUERY *view;
4155 UINT r;
4157 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4158 if (r == ERROR_SUCCESS)
4160 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4161 msiobj_release(&view->hdr);
4162 if (r != ERROR_SUCCESS)
4163 return r;
4165 return ERROR_SUCCESS;
4168 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4170 UINT r;
4171 HKEY source;
4172 LPWSTR buffer;
4173 MSIMEDIADISK *disk;
4174 MSISOURCELISTINFO *info;
4176 r = RegCreateKeyW(hkey, szSourceList, &source);
4177 if (r != ERROR_SUCCESS)
4178 return r;
4180 RegCloseKey(source);
4182 buffer = strrchrW(package->PackagePath, '\\') + 1;
4183 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4184 package->Context, MSICODE_PRODUCT,
4185 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4186 if (r != ERROR_SUCCESS)
4187 return r;
4189 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4190 package->Context, MSICODE_PRODUCT,
4191 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4192 if (r != ERROR_SUCCESS)
4193 return r;
4195 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4196 package->Context, MSICODE_PRODUCT,
4197 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4198 if (r != ERROR_SUCCESS)
4199 return r;
4201 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4203 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4204 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4205 info->options, info->value);
4206 else
4207 MsiSourceListSetInfoW(package->ProductCode, NULL,
4208 info->context, info->options,
4209 info->property, info->value);
4212 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4214 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4215 disk->context, disk->options,
4216 disk->disk_id, disk->volume_label, disk->disk_prompt);
4219 return ERROR_SUCCESS;
4222 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4224 static const WCHAR szARPProductIcon[] =
4225 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4226 static const WCHAR szAssignment[] =
4227 {'A','s','s','i','g','n','m','e','n','t',0};
4228 static const WCHAR szAdvertiseFlags[] =
4229 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4230 static const WCHAR szClients[] =
4231 {'C','l','i','e','n','t','s',0};
4232 static const WCHAR szColon[] = {':',0};
4233 MSIHANDLE hdb, suminfo;
4234 WCHAR *buffer, *ptr, guids[MAX_PATH], packcode[SQUASHED_GUID_SIZE];
4235 DWORD langid, size;
4236 UINT r;
4238 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4239 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4240 msi_free(buffer);
4242 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4243 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4245 /* FIXME */
4246 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4248 buffer = msi_dup_property(package->db, szARPProductIcon);
4249 if (buffer)
4251 LPWSTR path = msi_build_icon_path(package, buffer);
4252 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4253 msi_free(path);
4254 msi_free(buffer);
4257 buffer = msi_dup_property(package->db, szProductVersion);
4258 if (buffer)
4260 DWORD verdword = msi_version_str_to_dword(buffer);
4261 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4262 msi_free(buffer);
4265 msi_reg_set_val_dword(hkey, szAssignment, 0);
4266 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4267 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4268 msi_reg_set_val_str(hkey, szClients, szColon);
4270 hdb = alloc_msihandle(&package->db->hdr);
4271 if (!hdb)
4272 return ERROR_NOT_ENOUGH_MEMORY;
4274 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4275 MsiCloseHandle(hdb);
4276 if (r != ERROR_SUCCESS)
4277 goto done;
4279 size = MAX_PATH;
4280 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4281 NULL, guids, &size);
4282 if (r != ERROR_SUCCESS)
4283 goto done;
4285 ptr = strchrW(guids, ';');
4286 if (ptr) *ptr = 0;
4287 squash_guid(guids, packcode);
4288 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4290 done:
4291 MsiCloseHandle(suminfo);
4292 return ERROR_SUCCESS;
4295 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4297 UINT r;
4298 HKEY hkey;
4299 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4301 upgrade = msi_dup_property(package->db, szUpgradeCode);
4302 if (!upgrade)
4303 return ERROR_SUCCESS;
4305 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4306 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4307 else
4308 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4310 if (r != ERROR_SUCCESS)
4312 WARN("failed to open upgrade code key\n");
4313 msi_free(upgrade);
4314 return ERROR_SUCCESS;
4316 squash_guid(package->ProductCode, squashed_pc);
4317 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4318 RegCloseKey(hkey);
4319 msi_free(upgrade);
4320 return ERROR_SUCCESS;
4323 static BOOL msi_check_publish(MSIPACKAGE *package)
4325 MSIFEATURE *feature;
4327 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4329 feature->Action = msi_get_feature_action( package, feature );
4330 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4331 return TRUE;
4334 return FALSE;
4337 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4339 MSIFEATURE *feature;
4341 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4343 feature->Action = msi_get_feature_action( package, feature );
4344 if (feature->Action != INSTALLSTATE_ABSENT)
4345 return FALSE;
4348 return TRUE;
4351 static UINT msi_publish_patches( MSIPACKAGE *package )
4353 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4354 WCHAR patch_squashed[GUID_SIZE];
4355 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4356 LONG res;
4357 MSIPATCHINFO *patch;
4358 UINT r;
4359 WCHAR *p, *all_patches = NULL;
4360 DWORD len = 0;
4362 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4363 if (r != ERROR_SUCCESS)
4364 return ERROR_FUNCTION_FAILED;
4366 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4367 if (res != ERROR_SUCCESS)
4369 r = ERROR_FUNCTION_FAILED;
4370 goto done;
4373 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4374 if (r != ERROR_SUCCESS)
4375 goto done;
4377 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4379 squash_guid( patch->patchcode, patch_squashed );
4380 len += strlenW( patch_squashed ) + 1;
4383 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4384 if (!all_patches)
4385 goto done;
4387 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4389 HKEY patch_key;
4391 squash_guid( patch->patchcode, p );
4392 p += strlenW( p ) + 1;
4394 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4395 (const BYTE *)patch->transforms,
4396 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4397 if (res != ERROR_SUCCESS)
4398 goto done;
4400 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4401 if (r != ERROR_SUCCESS)
4402 goto done;
4404 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4405 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4406 RegCloseKey( patch_key );
4407 if (res != ERROR_SUCCESS)
4408 goto done;
4410 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4412 res = GetLastError();
4413 ERR("Unable to copy patch package %d\n", res);
4414 goto done;
4416 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4417 if (res != ERROR_SUCCESS)
4418 goto done;
4420 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
4421 sizeof(patch->state) );
4422 if (res != ERROR_SUCCESS)
4424 RegCloseKey( patch_key );
4425 goto done;
4428 res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4429 sizeof(patch->uninstallable) );
4430 RegCloseKey( patch_key );
4431 if (res != ERROR_SUCCESS)
4432 goto done;
4435 all_patches[len] = 0;
4436 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4437 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4438 if (res != ERROR_SUCCESS)
4439 goto done;
4441 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4442 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4443 if (res != ERROR_SUCCESS)
4444 r = ERROR_FUNCTION_FAILED;
4446 done:
4447 RegCloseKey( product_patches_key );
4448 RegCloseKey( patches_key );
4449 RegCloseKey( product_key );
4450 msi_free( all_patches );
4451 return r;
4454 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4456 UINT rc;
4457 HKEY hukey = NULL, hudkey = NULL;
4458 MSIRECORD *uirow;
4460 if (!list_empty(&package->patches))
4462 rc = msi_publish_patches(package);
4463 if (rc != ERROR_SUCCESS)
4464 goto end;
4467 /* FIXME: also need to publish if the product is in advertise mode */
4468 if (!msi_check_publish(package))
4469 return ERROR_SUCCESS;
4471 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4472 &hukey, TRUE);
4473 if (rc != ERROR_SUCCESS)
4474 goto end;
4476 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4477 NULL, &hudkey, TRUE);
4478 if (rc != ERROR_SUCCESS)
4479 goto end;
4481 rc = msi_publish_upgrade_code(package);
4482 if (rc != ERROR_SUCCESS)
4483 goto end;
4485 rc = msi_publish_product_properties(package, hukey);
4486 if (rc != ERROR_SUCCESS)
4487 goto end;
4489 rc = msi_publish_sourcelist(package, hukey);
4490 if (rc != ERROR_SUCCESS)
4491 goto end;
4493 rc = msi_publish_icons(package);
4495 end:
4496 uirow = MSI_CreateRecord( 1 );
4497 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4498 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4499 msiobj_release( &uirow->hdr );
4501 RegCloseKey(hukey);
4502 RegCloseKey(hudkey);
4503 return rc;
4506 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4508 WCHAR *filename, *ptr, *folder, *ret;
4509 const WCHAR *dirprop;
4511 filename = msi_dup_record_field( row, 2 );
4512 if (filename && (ptr = strchrW( filename, '|' )))
4513 ptr++;
4514 else
4515 ptr = filename;
4517 dirprop = MSI_RecordGetString( row, 3 );
4518 if (dirprop)
4520 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4521 if (!folder) folder = msi_dup_property( package->db, dirprop );
4523 else
4524 folder = msi_dup_property( package->db, szWindowsFolder );
4526 if (!folder)
4528 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4529 msi_free( filename );
4530 return NULL;
4533 ret = msi_build_directory_name( 2, folder, ptr );
4535 msi_free( filename );
4536 msi_free( folder );
4537 return ret;
4540 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4542 MSIPACKAGE *package = param;
4543 LPCWSTR component, section, key, value, identifier;
4544 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4545 MSIRECORD * uirow;
4546 INT action;
4547 MSICOMPONENT *comp;
4549 component = MSI_RecordGetString(row, 8);
4550 comp = msi_get_loaded_component(package,component);
4551 if (!comp)
4552 return ERROR_SUCCESS;
4554 comp->Action = msi_get_component_action( package, comp );
4555 if (comp->Action != INSTALLSTATE_LOCAL)
4557 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4558 return ERROR_SUCCESS;
4561 identifier = MSI_RecordGetString(row,1);
4562 section = MSI_RecordGetString(row,4);
4563 key = MSI_RecordGetString(row,5);
4564 value = MSI_RecordGetString(row,6);
4565 action = MSI_RecordGetInteger(row,7);
4567 deformat_string(package,section,&deformated_section);
4568 deformat_string(package,key,&deformated_key);
4569 deformat_string(package,value,&deformated_value);
4571 fullname = get_ini_file_name(package, row);
4573 if (action == 0)
4575 TRACE("Adding value %s to section %s in %s\n",
4576 debugstr_w(deformated_key), debugstr_w(deformated_section),
4577 debugstr_w(fullname));
4578 WritePrivateProfileStringW(deformated_section, deformated_key,
4579 deformated_value, fullname);
4581 else if (action == 1)
4583 WCHAR returned[10];
4584 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4585 returned, 10, fullname);
4586 if (returned[0] == 0)
4588 TRACE("Adding value %s to section %s in %s\n",
4589 debugstr_w(deformated_key), debugstr_w(deformated_section),
4590 debugstr_w(fullname));
4592 WritePrivateProfileStringW(deformated_section, deformated_key,
4593 deformated_value, fullname);
4596 else if (action == 3)
4597 FIXME("Append to existing section not yet implemented\n");
4599 uirow = MSI_CreateRecord(4);
4600 MSI_RecordSetStringW(uirow,1,identifier);
4601 MSI_RecordSetStringW(uirow,2,deformated_section);
4602 MSI_RecordSetStringW(uirow,3,deformated_key);
4603 MSI_RecordSetStringW(uirow,4,deformated_value);
4604 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4605 msiobj_release( &uirow->hdr );
4607 msi_free(fullname);
4608 msi_free(deformated_key);
4609 msi_free(deformated_value);
4610 msi_free(deformated_section);
4611 return ERROR_SUCCESS;
4614 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4616 static const WCHAR query[] = {
4617 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4618 '`','I','n','i','F','i','l','e','`',0};
4619 MSIQUERY *view;
4620 UINT rc;
4622 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4623 if (rc != ERROR_SUCCESS)
4624 return ERROR_SUCCESS;
4626 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4627 msiobj_release(&view->hdr);
4628 return rc;
4631 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4633 MSIPACKAGE *package = param;
4634 LPCWSTR component, section, key, value, identifier;
4635 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4636 MSICOMPONENT *comp;
4637 MSIRECORD *uirow;
4638 INT action;
4640 component = MSI_RecordGetString( row, 8 );
4641 comp = msi_get_loaded_component( package, component );
4642 if (!comp)
4643 return ERROR_SUCCESS;
4645 comp->Action = msi_get_component_action( package, comp );
4646 if (comp->Action != INSTALLSTATE_ABSENT)
4648 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4649 return ERROR_SUCCESS;
4652 identifier = MSI_RecordGetString( row, 1 );
4653 section = MSI_RecordGetString( row, 4 );
4654 key = MSI_RecordGetString( row, 5 );
4655 value = MSI_RecordGetString( row, 6 );
4656 action = MSI_RecordGetInteger( row, 7 );
4658 deformat_string( package, section, &deformated_section );
4659 deformat_string( package, key, &deformated_key );
4660 deformat_string( package, value, &deformated_value );
4662 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4664 filename = get_ini_file_name( package, row );
4666 TRACE("Removing key %s from section %s in %s\n",
4667 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4669 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4671 WARN("Unable to remove key %u\n", GetLastError());
4673 msi_free( filename );
4675 else
4676 FIXME("Unsupported action %d\n", action);
4679 uirow = MSI_CreateRecord( 4 );
4680 MSI_RecordSetStringW( uirow, 1, identifier );
4681 MSI_RecordSetStringW( uirow, 2, deformated_section );
4682 MSI_RecordSetStringW( uirow, 3, deformated_key );
4683 MSI_RecordSetStringW( uirow, 4, deformated_value );
4684 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4685 msiobj_release( &uirow->hdr );
4687 msi_free( deformated_key );
4688 msi_free( deformated_value );
4689 msi_free( deformated_section );
4690 return ERROR_SUCCESS;
4693 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4695 MSIPACKAGE *package = param;
4696 LPCWSTR component, section, key, value, identifier;
4697 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4698 MSICOMPONENT *comp;
4699 MSIRECORD *uirow;
4700 INT action;
4702 component = MSI_RecordGetString( row, 8 );
4703 comp = msi_get_loaded_component( package, component );
4704 if (!comp)
4705 return ERROR_SUCCESS;
4707 comp->Action = msi_get_component_action( package, comp );
4708 if (comp->Action != INSTALLSTATE_LOCAL)
4710 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4711 return ERROR_SUCCESS;
4714 identifier = MSI_RecordGetString( row, 1 );
4715 section = MSI_RecordGetString( row, 4 );
4716 key = MSI_RecordGetString( row, 5 );
4717 value = MSI_RecordGetString( row, 6 );
4718 action = MSI_RecordGetInteger( row, 7 );
4720 deformat_string( package, section, &deformated_section );
4721 deformat_string( package, key, &deformated_key );
4722 deformat_string( package, value, &deformated_value );
4724 if (action == msidbIniFileActionRemoveLine)
4726 filename = get_ini_file_name( package, row );
4728 TRACE("Removing key %s from section %s in %s\n",
4729 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4731 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4733 WARN("Unable to remove key %u\n", GetLastError());
4735 msi_free( filename );
4737 else
4738 FIXME("Unsupported action %d\n", action);
4740 uirow = MSI_CreateRecord( 4 );
4741 MSI_RecordSetStringW( uirow, 1, identifier );
4742 MSI_RecordSetStringW( uirow, 2, deformated_section );
4743 MSI_RecordSetStringW( uirow, 3, deformated_key );
4744 MSI_RecordSetStringW( uirow, 4, deformated_value );
4745 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4746 msiobj_release( &uirow->hdr );
4748 msi_free( deformated_key );
4749 msi_free( deformated_value );
4750 msi_free( deformated_section );
4751 return ERROR_SUCCESS;
4754 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4756 static const WCHAR query[] = {
4757 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4758 '`','I','n','i','F','i','l','e','`',0};
4759 static const WCHAR remove_query[] = {
4760 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4761 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4762 MSIQUERY *view;
4763 UINT rc;
4765 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4766 if (rc == ERROR_SUCCESS)
4768 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4769 msiobj_release( &view->hdr );
4770 if (rc != ERROR_SUCCESS)
4771 return rc;
4773 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4774 if (rc == ERROR_SUCCESS)
4776 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4777 msiobj_release( &view->hdr );
4778 if (rc != ERROR_SUCCESS)
4779 return rc;
4781 return ERROR_SUCCESS;
4784 static void register_dll( const WCHAR *dll, BOOL unregister )
4786 static const WCHAR regW[] =
4787 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4788 static const WCHAR unregW[] =
4789 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4790 PROCESS_INFORMATION pi;
4791 STARTUPINFOW si;
4792 WCHAR *cmd;
4794 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4796 if (unregister) sprintfW( cmd, unregW, dll );
4797 else sprintfW( cmd, regW, dll );
4799 memset( &si, 0, sizeof(STARTUPINFOW) );
4800 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4802 CloseHandle( pi.hThread );
4803 msi_dialog_check_messages( pi.hProcess );
4804 CloseHandle( pi.hProcess );
4806 msi_free( cmd );
4809 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4811 MSIPACKAGE *package = param;
4812 LPCWSTR filename;
4813 MSIFILE *file;
4814 MSIRECORD *uirow;
4816 filename = MSI_RecordGetString( row, 1 );
4817 file = msi_get_loaded_file( package, filename );
4818 if (!file)
4820 WARN("unable to find file %s\n", debugstr_w(filename));
4821 return ERROR_SUCCESS;
4823 file->Component->Action = msi_get_component_action( package, file->Component );
4824 if (file->Component->Action != INSTALLSTATE_LOCAL)
4826 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4827 return ERROR_SUCCESS;
4830 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4831 register_dll( file->TargetPath, FALSE );
4833 uirow = MSI_CreateRecord( 2 );
4834 MSI_RecordSetStringW( uirow, 1, file->File );
4835 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4836 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4837 msiobj_release( &uirow->hdr );
4839 return ERROR_SUCCESS;
4842 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4844 static const WCHAR query[] = {
4845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4846 '`','S','e','l','f','R','e','g','`',0};
4847 MSIQUERY *view;
4848 UINT rc;
4850 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4851 if (rc != ERROR_SUCCESS)
4852 return ERROR_SUCCESS;
4854 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4855 msiobj_release(&view->hdr);
4856 return rc;
4859 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4861 MSIPACKAGE *package = param;
4862 LPCWSTR filename;
4863 MSIFILE *file;
4864 MSIRECORD *uirow;
4866 filename = MSI_RecordGetString( row, 1 );
4867 file = msi_get_loaded_file( package, filename );
4868 if (!file)
4870 WARN("unable to find file %s\n", debugstr_w(filename));
4871 return ERROR_SUCCESS;
4873 file->Component->Action = msi_get_component_action( package, file->Component );
4874 if (file->Component->Action != INSTALLSTATE_ABSENT)
4876 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4877 return ERROR_SUCCESS;
4880 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4881 register_dll( file->TargetPath, TRUE );
4883 uirow = MSI_CreateRecord( 2 );
4884 MSI_RecordSetStringW( uirow, 1, file->File );
4885 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4886 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4887 msiobj_release( &uirow->hdr );
4889 return ERROR_SUCCESS;
4892 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4894 static const WCHAR query[] = {
4895 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4896 '`','S','e','l','f','R','e','g','`',0};
4897 MSIQUERY *view;
4898 UINT rc;
4900 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4901 if (rc != ERROR_SUCCESS)
4902 return ERROR_SUCCESS;
4904 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4905 msiobj_release( &view->hdr );
4906 return rc;
4909 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4911 MSIFEATURE *feature;
4912 UINT rc;
4913 HKEY hkey = NULL, userdata = NULL;
4915 if (!msi_check_publish(package))
4916 return ERROR_SUCCESS;
4918 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4919 &hkey, TRUE);
4920 if (rc != ERROR_SUCCESS)
4921 goto end;
4923 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4924 &userdata, TRUE);
4925 if (rc != ERROR_SUCCESS)
4926 goto end;
4928 /* here the guids are base 85 encoded */
4929 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4931 ComponentList *cl;
4932 LPWSTR data = NULL;
4933 GUID clsid;
4934 INT size;
4935 BOOL absent = FALSE;
4936 MSIRECORD *uirow;
4938 if (feature->Level <= 0) continue;
4940 if (feature->Action != INSTALLSTATE_LOCAL &&
4941 feature->Action != INSTALLSTATE_SOURCE &&
4942 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4944 size = 1;
4945 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4947 size += 21;
4949 if (feature->Feature_Parent)
4950 size += strlenW( feature->Feature_Parent )+2;
4952 data = msi_alloc(size * sizeof(WCHAR));
4954 data[0] = 0;
4955 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4957 MSICOMPONENT* component = cl->component;
4958 WCHAR buf[21];
4960 buf[0] = 0;
4961 if (component->ComponentId)
4963 TRACE("From %s\n",debugstr_w(component->ComponentId));
4964 CLSIDFromString(component->ComponentId, &clsid);
4965 encode_base85_guid(&clsid,buf);
4966 TRACE("to %s\n",debugstr_w(buf));
4967 strcatW(data,buf);
4971 if (feature->Feature_Parent)
4973 static const WCHAR sep[] = {'\2',0};
4974 strcatW(data,sep);
4975 strcatW(data,feature->Feature_Parent);
4978 msi_reg_set_val_str( userdata, feature->Feature, data );
4979 msi_free(data);
4981 size = 0;
4982 if (feature->Feature_Parent)
4983 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4984 if (!absent)
4986 size += sizeof(WCHAR);
4987 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4988 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4990 else
4992 size += 2*sizeof(WCHAR);
4993 data = msi_alloc(size);
4994 data[0] = 0x6;
4995 data[1] = 0;
4996 if (feature->Feature_Parent)
4997 strcpyW( &data[1], feature->Feature_Parent );
4998 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4999 (LPBYTE)data,size);
5000 msi_free(data);
5003 /* the UI chunk */
5004 uirow = MSI_CreateRecord( 1 );
5005 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5006 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5007 msiobj_release( &uirow->hdr );
5008 /* FIXME: call msi_ui_progress? */
5011 end:
5012 RegCloseKey(hkey);
5013 RegCloseKey(userdata);
5014 return rc;
5017 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5019 UINT r;
5020 HKEY hkey;
5021 MSIRECORD *uirow;
5023 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5025 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5026 &hkey, FALSE);
5027 if (r == ERROR_SUCCESS)
5029 RegDeleteValueW(hkey, feature->Feature);
5030 RegCloseKey(hkey);
5033 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5034 &hkey, FALSE);
5035 if (r == ERROR_SUCCESS)
5037 RegDeleteValueW(hkey, feature->Feature);
5038 RegCloseKey(hkey);
5041 uirow = MSI_CreateRecord( 1 );
5042 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5043 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5044 msiobj_release( &uirow->hdr );
5046 return ERROR_SUCCESS;
5049 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5051 MSIFEATURE *feature;
5053 if (!msi_check_unpublish(package))
5054 return ERROR_SUCCESS;
5056 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5058 msi_unpublish_feature(package, feature);
5061 return ERROR_SUCCESS;
5064 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5066 SYSTEMTIME systime;
5067 DWORD size, langid;
5068 WCHAR date[9], *val, *buffer;
5069 const WCHAR *prop, *key;
5071 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5072 static const WCHAR modpath_fmt[] =
5073 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5074 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5075 static const WCHAR szModifyPath[] =
5076 {'M','o','d','i','f','y','P','a','t','h',0};
5077 static const WCHAR szUninstallString[] =
5078 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5079 static const WCHAR szEstimatedSize[] =
5080 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5081 static const WCHAR szDisplayVersion[] =
5082 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5083 static const WCHAR szInstallSource[] =
5084 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5085 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5086 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5087 static const WCHAR szAuthorizedCDFPrefix[] =
5088 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5089 static const WCHAR szARPCONTACT[] =
5090 {'A','R','P','C','O','N','T','A','C','T',0};
5091 static const WCHAR szContact[] =
5092 {'C','o','n','t','a','c','t',0};
5093 static const WCHAR szARPCOMMENTS[] =
5094 {'A','R','P','C','O','M','M','E','N','T','S',0};
5095 static const WCHAR szComments[] =
5096 {'C','o','m','m','e','n','t','s',0};
5097 static const WCHAR szProductName[] =
5098 {'P','r','o','d','u','c','t','N','a','m','e',0};
5099 static const WCHAR szDisplayName[] =
5100 {'D','i','s','p','l','a','y','N','a','m','e',0};
5101 static const WCHAR szARPHELPLINK[] =
5102 {'A','R','P','H','E','L','P','L','I','N','K',0};
5103 static const WCHAR szHelpLink[] =
5104 {'H','e','l','p','L','i','n','k',0};
5105 static const WCHAR szARPHELPTELEPHONE[] =
5106 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5107 static const WCHAR szHelpTelephone[] =
5108 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5109 static const WCHAR szARPINSTALLLOCATION[] =
5110 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5111 static const WCHAR szManufacturer[] =
5112 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5113 static const WCHAR szPublisher[] =
5114 {'P','u','b','l','i','s','h','e','r',0};
5115 static const WCHAR szARPREADME[] =
5116 {'A','R','P','R','E','A','D','M','E',0};
5117 static const WCHAR szReadme[] =
5118 {'R','e','a','d','M','e',0};
5119 static const WCHAR szARPSIZE[] =
5120 {'A','R','P','S','I','Z','E',0};
5121 static const WCHAR szSize[] =
5122 {'S','i','z','e',0};
5123 static const WCHAR szARPURLINFOABOUT[] =
5124 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5125 static const WCHAR szURLInfoAbout[] =
5126 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5127 static const WCHAR szARPURLUPDATEINFO[] =
5128 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5129 static const WCHAR szURLUpdateInfo[] =
5130 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5131 static const WCHAR szARPSYSTEMCOMPONENT[] =
5132 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5133 static const WCHAR szSystemComponent[] =
5134 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5136 static const WCHAR *propval[] = {
5137 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5138 szARPCONTACT, szContact,
5139 szARPCOMMENTS, szComments,
5140 szProductName, szDisplayName,
5141 szARPHELPLINK, szHelpLink,
5142 szARPHELPTELEPHONE, szHelpTelephone,
5143 szARPINSTALLLOCATION, szInstallLocation,
5144 szSourceDir, szInstallSource,
5145 szManufacturer, szPublisher,
5146 szARPREADME, szReadme,
5147 szARPSIZE, szSize,
5148 szARPURLINFOABOUT, szURLInfoAbout,
5149 szARPURLUPDATEINFO, szURLUpdateInfo,
5150 NULL
5152 const WCHAR **p = propval;
5154 while (*p)
5156 prop = *p++;
5157 key = *p++;
5158 val = msi_dup_property(package->db, prop);
5159 msi_reg_set_val_str(hkey, key, val);
5160 msi_free(val);
5163 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5164 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5166 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5168 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5169 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5170 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5171 msi_free(buffer);
5173 /* FIXME: Write real Estimated Size when we have it */
5174 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5176 GetLocalTime(&systime);
5177 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5178 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5180 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5181 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5183 buffer = msi_dup_property(package->db, szProductVersion);
5184 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5185 if (buffer)
5187 DWORD verdword = msi_version_str_to_dword(buffer);
5189 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5190 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5191 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5192 msi_free(buffer);
5195 return ERROR_SUCCESS;
5198 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5200 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5201 MSIRECORD *uirow;
5202 HKEY hkey, props, upgrade_key;
5203 UINT rc;
5205 /* FIXME: also need to publish if the product is in advertise mode */
5206 if (!msi_check_publish(package))
5207 return ERROR_SUCCESS;
5209 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5210 if (rc != ERROR_SUCCESS)
5211 return rc;
5213 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5214 if (rc != ERROR_SUCCESS)
5215 goto done;
5217 rc = msi_publish_install_properties(package, hkey);
5218 if (rc != ERROR_SUCCESS)
5219 goto done;
5221 rc = msi_publish_install_properties(package, props);
5222 if (rc != ERROR_SUCCESS)
5223 goto done;
5225 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5226 if (upgrade_code)
5228 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5229 if (rc == ERROR_SUCCESS)
5231 squash_guid( package->ProductCode, squashed_pc );
5232 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5233 RegCloseKey( upgrade_key );
5235 msi_free( upgrade_code );
5237 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5238 package->delete_on_close = FALSE;
5240 done:
5241 uirow = MSI_CreateRecord( 1 );
5242 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5243 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5244 msiobj_release( &uirow->hdr );
5246 RegCloseKey(hkey);
5247 return ERROR_SUCCESS;
5250 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5252 return execute_script(package, SCRIPT_INSTALL);
5255 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5257 MSIPACKAGE *package = param;
5258 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5259 WCHAR *p, *icon_path;
5261 if (!icon) return ERROR_SUCCESS;
5262 if ((icon_path = msi_build_icon_path( package, icon )))
5264 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5265 DeleteFileW( icon_path );
5266 if ((p = strrchrW( icon_path, '\\' )))
5268 *p = 0;
5269 RemoveDirectoryW( icon_path );
5271 msi_free( icon_path );
5273 return ERROR_SUCCESS;
5276 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5278 static const WCHAR query[]= {
5279 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5280 MSIQUERY *view;
5281 UINT r;
5283 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5284 if (r == ERROR_SUCCESS)
5286 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5287 msiobj_release( &view->hdr );
5288 if (r != ERROR_SUCCESS)
5289 return r;
5291 return ERROR_SUCCESS;
5294 static void remove_product_upgrade_code( MSIPACKAGE *package )
5296 WCHAR *code, product[SQUASHED_GUID_SIZE];
5297 HKEY hkey;
5298 LONG res;
5299 DWORD count;
5301 squash_guid( package->ProductCode, product );
5302 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5304 WARN( "upgrade code not found\n" );
5305 return;
5307 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5309 RegDeleteValueW( hkey, product );
5310 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5311 RegCloseKey( hkey );
5312 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5314 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5316 RegDeleteValueW( hkey, product );
5317 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5318 RegCloseKey( hkey );
5319 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5321 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5323 RegDeleteValueW( hkey, product );
5324 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5325 RegCloseKey( hkey );
5326 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5329 msi_free( code );
5332 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5334 MSIPATCHINFO *patch;
5336 MSIREG_DeleteProductKey(package->ProductCode);
5337 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5338 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5340 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5341 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5342 MSIREG_DeleteUserProductKey(package->ProductCode);
5343 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5345 remove_product_upgrade_code( package );
5347 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5349 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5350 if (!strcmpW( package->ProductCode, patch->products ))
5352 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5353 patch->delete_on_close = TRUE;
5355 /* FIXME: remove local patch package if this is the last product */
5357 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5358 package->delete_on_close = TRUE;
5360 msi_unpublish_icons( package );
5361 return ERROR_SUCCESS;
5364 static BOOL is_full_uninstall( MSIPACKAGE *package )
5366 MSIFEATURE *feature;
5368 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5370 if (feature->Action != INSTALLSTATE_ABSENT &&
5371 (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
5372 return FALSE;
5375 return TRUE;
5378 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5380 UINT rc;
5382 /* first do the same as an InstallExecute */
5383 rc = execute_script(package, SCRIPT_INSTALL);
5384 if (rc != ERROR_SUCCESS)
5385 return rc;
5387 /* then handle commit actions */
5388 rc = execute_script(package, SCRIPT_COMMIT);
5389 if (rc != ERROR_SUCCESS)
5390 return rc;
5392 if (is_full_uninstall(package))
5393 rc = ACTION_UnpublishProduct(package);
5395 return rc;
5398 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5400 static const WCHAR RunOnce[] = {
5401 'S','o','f','t','w','a','r','e','\\',
5402 'M','i','c','r','o','s','o','f','t','\\',
5403 'W','i','n','d','o','w','s','\\',
5404 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5405 'R','u','n','O','n','c','e',0};
5406 static const WCHAR InstallRunOnce[] = {
5407 'S','o','f','t','w','a','r','e','\\',
5408 'M','i','c','r','o','s','o','f','t','\\',
5409 'W','i','n','d','o','w','s','\\',
5410 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5411 'I','n','s','t','a','l','l','e','r','\\',
5412 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5414 static const WCHAR msiexec_fmt[] = {
5415 '%','s',
5416 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5417 '\"','%','s','\"',0};
5418 static const WCHAR install_fmt[] = {
5419 '/','I',' ','\"','%','s','\"',' ',
5420 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5421 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5422 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5423 HKEY hkey;
5425 squash_guid( package->ProductCode, squashed_pc );
5427 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5428 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5429 snprintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), msiexec_fmt, sysdir, squashed_pc );
5431 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5432 RegCloseKey(hkey);
5434 TRACE("Reboot command %s\n",debugstr_w(buffer));
5436 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5437 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5439 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5440 RegCloseKey(hkey);
5442 return ERROR_INSTALL_SUSPEND;
5445 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5447 DWORD attrib;
5448 UINT rc;
5451 * We are currently doing what should be done here in the top level Install
5452 * however for Administrative and uninstalls this step will be needed
5454 if (!package->PackagePath)
5455 return ERROR_SUCCESS;
5457 msi_set_sourcedir_props(package, TRUE);
5459 attrib = GetFileAttributesW(package->db->path);
5460 if (attrib == INVALID_FILE_ATTRIBUTES)
5462 MSIRECORD *record;
5463 LPWSTR prompt;
5464 DWORD size = 0;
5466 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5467 package->Context, MSICODE_PRODUCT,
5468 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5469 if (rc == ERROR_MORE_DATA)
5471 prompt = msi_alloc(size * sizeof(WCHAR));
5472 MsiSourceListGetInfoW(package->ProductCode, NULL,
5473 package->Context, MSICODE_PRODUCT,
5474 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5476 else
5477 prompt = strdupW(package->db->path);
5479 record = MSI_CreateRecord(2);
5480 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5481 MSI_RecordSetStringW(record, 2, prompt);
5482 msi_free(prompt);
5483 while(attrib == INVALID_FILE_ATTRIBUTES)
5485 MSI_RecordSetStringW(record, 0, NULL);
5486 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5487 if (rc == IDCANCEL)
5488 return ERROR_INSTALL_USEREXIT;
5489 attrib = GetFileAttributesW(package->db->path);
5491 rc = ERROR_SUCCESS;
5493 else
5494 return ERROR_SUCCESS;
5496 return rc;
5499 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5501 HKEY hkey = 0;
5502 LPWSTR buffer, productid = NULL;
5503 UINT i, rc = ERROR_SUCCESS;
5504 MSIRECORD *uirow;
5506 static const WCHAR szPropKeys[][80] =
5508 {'P','r','o','d','u','c','t','I','D',0},
5509 {'U','S','E','R','N','A','M','E',0},
5510 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5511 {0},
5514 static const WCHAR szRegKeys[][80] =
5516 {'P','r','o','d','u','c','t','I','D',0},
5517 {'R','e','g','O','w','n','e','r',0},
5518 {'R','e','g','C','o','m','p','a','n','y',0},
5519 {0},
5522 if (msi_check_unpublish(package))
5524 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5525 goto end;
5528 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5529 if (!productid)
5530 goto end;
5532 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5533 NULL, &hkey, TRUE);
5534 if (rc != ERROR_SUCCESS)
5535 goto end;
5537 for( i = 0; szPropKeys[i][0]; i++ )
5539 buffer = msi_dup_property( package->db, szPropKeys[i] );
5540 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5541 msi_free( buffer );
5544 end:
5545 uirow = MSI_CreateRecord( 1 );
5546 MSI_RecordSetStringW( uirow, 1, productid );
5547 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5548 msiobj_release( &uirow->hdr );
5550 msi_free(productid);
5551 RegCloseKey(hkey);
5552 return rc;
5555 static UINT iterate_properties(MSIRECORD *record, void *param)
5557 static const WCHAR prop_template[] =
5558 {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0};
5559 MSIRECORD *uirow;
5561 uirow = MSI_CloneRecord(record);
5562 if (!uirow) return ERROR_OUTOFMEMORY;
5563 MSI_RecordSetStringW(uirow, 0, prop_template);
5564 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5565 msiobj_release(&uirow->hdr);
5567 return ERROR_SUCCESS;
5571 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5573 static const WCHAR prop_query[] =
5574 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0};
5575 WCHAR *productname;
5576 WCHAR *action;
5577 WCHAR *info_template;
5578 MSIQUERY *view;
5579 MSIRECORD *uirow, *uirow_info;
5580 UINT rc;
5582 /* Send COMMONDATA and INFO messages. */
5583 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5584 uirow = MSI_CreateRecord(3);
5585 if (!uirow) return ERROR_OUTOFMEMORY;
5586 MSI_RecordSetStringW(uirow, 0, NULL);
5587 MSI_RecordSetInteger(uirow, 1, 0);
5588 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5589 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5590 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5591 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5592 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5594 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5596 uirow_info = MSI_CreateRecord(0);
5597 if (!uirow_info)
5599 msiobj_release(&uirow->hdr);
5600 return ERROR_OUTOFMEMORY;
5602 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5603 MSI_RecordSetStringW(uirow_info, 0, info_template);
5604 msi_free(info_template);
5605 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5606 msiobj_release(&uirow_info->hdr);
5609 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5611 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5612 MSI_RecordSetInteger(uirow, 1, 1);
5613 MSI_RecordSetStringW(uirow, 2, productname);
5614 MSI_RecordSetStringW(uirow, 3, NULL);
5615 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5616 msiobj_release(&uirow->hdr);
5618 package->LastActionResult = MSI_NULL_INTEGER;
5620 action = msi_dup_property(package->db, szEXECUTEACTION);
5621 if (!action) action = msi_strdupW(szINSTALL, strlenW(szINSTALL));
5623 /* Perform the action. Top-level actions trigger a sequence. */
5624 if (!strcmpW(action, szINSTALL))
5626 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5627 ui_actionstart(package, szINSTALL, NULL, NULL);
5628 ui_actioninfo(package, szINSTALL, TRUE, 0);
5629 uirow = MSI_CreateRecord(2);
5630 if (!uirow)
5632 rc = ERROR_OUTOFMEMORY;
5633 goto end;
5635 MSI_RecordSetStringW(uirow, 0, NULL);
5636 MSI_RecordSetStringW(uirow, 1, productname);
5637 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5638 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5639 msiobj_release(&uirow->hdr);
5641 /* Perform the installation. Always use the ExecuteSequence. */
5642 package->InWhatSequence |= SEQUENCE_EXEC;
5643 rc = ACTION_ProcessExecSequence(package);
5645 /* Send return value and INSTALLEND. */
5646 ui_actioninfo(package, szINSTALL, FALSE, !rc);
5647 uirow = MSI_CreateRecord(3);
5648 if (!uirow)
5650 rc = ERROR_OUTOFMEMORY;
5651 goto end;
5653 MSI_RecordSetStringW(uirow, 0, NULL);
5654 MSI_RecordSetStringW(uirow, 1, productname);
5655 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5656 MSI_RecordSetInteger(uirow, 3, !rc);
5657 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5658 msiobj_release(&uirow->hdr);
5660 else
5661 rc = ACTION_PerformAction(package, action);
5663 /* Send all set properties. */
5664 if (!MSI_OpenQuery(package->db, &view, prop_query))
5666 MSI_IterateRecords(view, NULL, iterate_properties, package);
5667 msiobj_release(&view->hdr);
5670 /* And finally, toggle the cancel off and on. */
5671 uirow = MSI_CreateRecord(2);
5672 if (!uirow)
5674 rc = ERROR_OUTOFMEMORY;
5675 goto end;
5677 MSI_RecordSetStringW(uirow, 0, NULL);
5678 MSI_RecordSetInteger(uirow, 1, 2);
5679 MSI_RecordSetInteger(uirow, 2, 0);
5680 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5681 MSI_RecordSetInteger(uirow, 2, 1);
5682 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5683 msiobj_release(&uirow->hdr);
5685 end:
5686 msi_free(productname);
5687 msi_free(action);
5688 return rc;
5691 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5693 msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1);
5694 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5696 package->InWhatSequence |= SEQUENCE_UI;
5697 return ACTION_ProcessUISequence(package);
5699 else
5700 return ACTION_ExecuteAction(package);
5703 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5705 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5706 WCHAR productid_85[21], component_85[21], *ret;
5707 GUID clsid;
5708 DWORD sz;
5710 /* > is used if there is a component GUID and < if not. */
5712 productid_85[0] = 0;
5713 component_85[0] = 0;
5714 CLSIDFromString( package->ProductCode, &clsid );
5716 encode_base85_guid( &clsid, productid_85 );
5717 if (component)
5719 CLSIDFromString( component->ComponentId, &clsid );
5720 encode_base85_guid( &clsid, component_85 );
5723 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5724 debugstr_w(component_85));
5726 sz = 20 + strlenW( feature ) + 20 + 3;
5727 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5728 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5729 return ret;
5732 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5734 MSIPACKAGE *package = param;
5735 LPCWSTR compgroupid, component, feature, qualifier, text;
5736 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5737 HKEY hkey = NULL;
5738 UINT rc;
5739 MSICOMPONENT *comp;
5740 MSIFEATURE *feat;
5741 DWORD sz;
5742 MSIRECORD *uirow;
5743 int len;
5745 feature = MSI_RecordGetString(rec, 5);
5746 feat = msi_get_loaded_feature(package, feature);
5747 if (!feat)
5748 return ERROR_SUCCESS;
5750 feat->Action = msi_get_feature_action( package, feat );
5751 if (feat->Action != INSTALLSTATE_LOCAL &&
5752 feat->Action != INSTALLSTATE_SOURCE &&
5753 feat->Action != INSTALLSTATE_ADVERTISED)
5755 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5756 return ERROR_SUCCESS;
5759 component = MSI_RecordGetString(rec, 3);
5760 comp = msi_get_loaded_component(package, component);
5761 if (!comp)
5762 return ERROR_SUCCESS;
5764 compgroupid = MSI_RecordGetString(rec,1);
5765 qualifier = MSI_RecordGetString(rec,2);
5767 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5768 if (rc != ERROR_SUCCESS)
5769 goto end;
5771 advertise = msi_create_component_advertise_string( package, comp, feature );
5772 text = MSI_RecordGetString( rec, 4 );
5773 if (text)
5775 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5776 strcpyW( p, advertise );
5777 strcatW( p, text );
5778 msi_free( advertise );
5779 advertise = p;
5781 existing = msi_reg_get_val_str( hkey, qualifier );
5783 sz = strlenW( advertise ) + 1;
5784 if (existing)
5786 for (p = existing; *p; p += len)
5788 len = strlenW( p ) + 1;
5789 if (strcmpW( advertise, p )) sz += len;
5792 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5794 rc = ERROR_OUTOFMEMORY;
5795 goto end;
5797 q = output;
5798 if (existing)
5800 for (p = existing; *p; p += len)
5802 len = strlenW( p ) + 1;
5803 if (strcmpW( advertise, p ))
5805 memcpy( q, p, len * sizeof(WCHAR) );
5806 q += len;
5810 strcpyW( q, advertise );
5811 q[strlenW( q ) + 1] = 0;
5813 msi_reg_set_val_multi_str( hkey, qualifier, output );
5815 end:
5816 RegCloseKey(hkey);
5817 msi_free( output );
5818 msi_free( advertise );
5819 msi_free( existing );
5821 /* the UI chunk */
5822 uirow = MSI_CreateRecord( 2 );
5823 MSI_RecordSetStringW( uirow, 1, compgroupid );
5824 MSI_RecordSetStringW( uirow, 2, qualifier);
5825 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5826 msiobj_release( &uirow->hdr );
5827 /* FIXME: call ui_progress? */
5829 return rc;
5833 * At present I am ignorning the advertised components part of this and only
5834 * focusing on the qualified component sets
5836 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5838 static const WCHAR query[] = {
5839 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5840 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5841 MSIQUERY *view;
5842 UINT rc;
5844 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5845 if (rc != ERROR_SUCCESS)
5846 return ERROR_SUCCESS;
5848 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5849 msiobj_release(&view->hdr);
5850 return rc;
5853 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5855 static const WCHAR szInstallerComponents[] = {
5856 'S','o','f','t','w','a','r','e','\\',
5857 'M','i','c','r','o','s','o','f','t','\\',
5858 'I','n','s','t','a','l','l','e','r','\\',
5859 'C','o','m','p','o','n','e','n','t','s','\\',0};
5861 MSIPACKAGE *package = param;
5862 LPCWSTR compgroupid, component, feature, qualifier;
5863 MSICOMPONENT *comp;
5864 MSIFEATURE *feat;
5865 MSIRECORD *uirow;
5866 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5867 LONG res;
5869 feature = MSI_RecordGetString( rec, 5 );
5870 feat = msi_get_loaded_feature( package, feature );
5871 if (!feat)
5872 return ERROR_SUCCESS;
5874 feat->Action = msi_get_feature_action( package, feat );
5875 if (feat->Action != INSTALLSTATE_ABSENT)
5877 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5878 return ERROR_SUCCESS;
5881 component = MSI_RecordGetString( rec, 3 );
5882 comp = msi_get_loaded_component( package, component );
5883 if (!comp)
5884 return ERROR_SUCCESS;
5886 compgroupid = MSI_RecordGetString( rec, 1 );
5887 qualifier = MSI_RecordGetString( rec, 2 );
5889 squash_guid( compgroupid, squashed );
5890 strcpyW( keypath, szInstallerComponents );
5891 strcatW( keypath, squashed );
5893 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5894 if (res != ERROR_SUCCESS)
5896 WARN("Unable to delete component key %d\n", res);
5899 uirow = MSI_CreateRecord( 2 );
5900 MSI_RecordSetStringW( uirow, 1, compgroupid );
5901 MSI_RecordSetStringW( uirow, 2, qualifier );
5902 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5903 msiobj_release( &uirow->hdr );
5905 return ERROR_SUCCESS;
5908 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5910 static const WCHAR query[] = {
5911 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5912 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5913 MSIQUERY *view;
5914 UINT rc;
5916 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5917 if (rc != ERROR_SUCCESS)
5918 return ERROR_SUCCESS;
5920 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5921 msiobj_release( &view->hdr );
5922 return rc;
5925 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5927 static const WCHAR query[] =
5928 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5929 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5930 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5931 MSIPACKAGE *package = param;
5932 MSICOMPONENT *component;
5933 MSIRECORD *row;
5934 MSIFILE *file;
5935 SC_HANDLE hscm = NULL, service = NULL;
5936 LPCWSTR comp, key;
5937 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5938 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5939 DWORD serv_type, start_type, err_control;
5940 BOOL is_vital;
5941 SERVICE_DESCRIPTIONW sd = {NULL};
5942 UINT ret = ERROR_SUCCESS;
5944 comp = MSI_RecordGetString( rec, 12 );
5945 component = msi_get_loaded_component( package, comp );
5946 if (!component)
5948 WARN("service component not found\n");
5949 goto done;
5951 component->Action = msi_get_component_action( package, component );
5952 if (component->Action != INSTALLSTATE_LOCAL)
5954 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5955 goto done;
5957 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5958 if (!hscm)
5960 ERR("Failed to open the SC Manager!\n");
5961 goto done;
5964 start_type = MSI_RecordGetInteger(rec, 5);
5965 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5966 goto done;
5968 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5969 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5970 serv_type = MSI_RecordGetInteger(rec, 4);
5971 err_control = MSI_RecordGetInteger(rec, 6);
5972 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5973 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5974 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5975 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5976 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5977 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5979 /* Should the complete install fail if CreateService fails? */
5980 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5982 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5983 CreateService (under Windows) would fail if not. */
5984 err_control &= ~msidbServiceInstallErrorControlVital;
5986 /* fetch the service path */
5987 row = MSI_QueryGetRecord(package->db, query, comp);
5988 if (!row)
5990 ERR("Query failed\n");
5991 goto done;
5993 if (!(key = MSI_RecordGetString(row, 6)))
5995 msiobj_release(&row->hdr);
5996 goto done;
5998 file = msi_get_loaded_file(package, key);
5999 msiobj_release(&row->hdr);
6000 if (!file)
6002 ERR("Failed to load the service file\n");
6003 goto done;
6006 if (!args || !args[0]) image_path = file->TargetPath;
6007 else
6009 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
6010 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6012 ret = ERROR_OUTOFMEMORY;
6013 goto done;
6016 strcpyW(image_path, file->TargetPath);
6017 strcatW(image_path, szSpace);
6018 strcatW(image_path, args);
6020 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6021 start_type, err_control, image_path, load_order,
6022 NULL, depends, serv_name, pass);
6024 if (!service)
6026 if (GetLastError() != ERROR_SERVICE_EXISTS)
6028 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6029 if (is_vital)
6030 ret = ERROR_INSTALL_FAILURE;
6034 else if (sd.lpDescription)
6036 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6037 WARN("failed to set service description %u\n", GetLastError());
6040 if (image_path != file->TargetPath) msi_free(image_path);
6041 done:
6042 if (service) CloseServiceHandle(service);
6043 if (hscm) CloseServiceHandle(hscm);
6044 msi_free(name);
6045 msi_free(disp);
6046 msi_free(sd.lpDescription);
6047 msi_free(load_order);
6048 msi_free(serv_name);
6049 msi_free(pass);
6050 msi_free(depends);
6051 msi_free(args);
6053 return ret;
6056 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6058 static const WCHAR query[] = {
6059 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6060 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6061 MSIQUERY *view;
6062 UINT rc;
6064 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6065 if (rc != ERROR_SUCCESS)
6066 return ERROR_SUCCESS;
6068 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6069 msiobj_release(&view->hdr);
6070 return rc;
6073 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6074 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6076 LPCWSTR *vector, *temp_vector;
6077 LPWSTR p, q;
6078 DWORD sep_len;
6080 static const WCHAR separator[] = {'[','~',']',0};
6082 *numargs = 0;
6083 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
6085 if (!args)
6086 return NULL;
6088 vector = msi_alloc(sizeof(LPWSTR));
6089 if (!vector)
6090 return NULL;
6092 p = args;
6095 (*numargs)++;
6096 vector[*numargs - 1] = p;
6098 if ((q = strstrW(p, separator)))
6100 *q = '\0';
6102 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6103 if (!temp_vector)
6105 msi_free(vector);
6106 return NULL;
6108 vector = temp_vector;
6110 p = q + sep_len;
6112 } while (q);
6114 return vector;
6117 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6119 MSIPACKAGE *package = param;
6120 MSICOMPONENT *comp;
6121 MSIRECORD *uirow;
6122 SC_HANDLE scm = NULL, service = NULL;
6123 LPCWSTR component, *vector = NULL;
6124 LPWSTR name, args, display_name = NULL;
6125 DWORD event, numargs, len, wait, dummy;
6126 UINT r = ERROR_FUNCTION_FAILED;
6127 SERVICE_STATUS_PROCESS status;
6128 ULONGLONG start_time;
6130 component = MSI_RecordGetString(rec, 6);
6131 comp = msi_get_loaded_component(package, component);
6132 if (!comp)
6133 return ERROR_SUCCESS;
6135 event = MSI_RecordGetInteger( rec, 3 );
6136 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6138 comp->Action = msi_get_component_action( package, comp );
6139 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6140 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6142 TRACE("not starting %s\n", debugstr_w(name));
6143 msi_free( name );
6144 return ERROR_SUCCESS;
6147 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6148 wait = MSI_RecordGetInteger(rec, 5);
6150 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6151 if (!scm)
6153 ERR("Failed to open the service control manager\n");
6154 goto done;
6157 len = 0;
6158 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6159 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6161 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6162 GetServiceDisplayNameW( scm, name, display_name, &len );
6165 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6166 if (!service)
6168 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6169 goto done;
6172 vector = msi_service_args_to_vector(args, &numargs);
6174 if (!StartServiceW(service, numargs, vector) &&
6175 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6177 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6178 goto done;
6181 r = ERROR_SUCCESS;
6182 if (wait)
6184 /* wait for at most 30 seconds for the service to be up and running */
6185 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6186 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6188 TRACE("failed to query service status (%u)\n", GetLastError());
6189 goto done;
6191 start_time = GetTickCount64();
6192 while (status.dwCurrentState == SERVICE_START_PENDING)
6194 if (GetTickCount64() - start_time > 30000) break;
6195 Sleep(1000);
6196 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6197 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6199 TRACE("failed to query service status (%u)\n", GetLastError());
6200 goto done;
6203 if (status.dwCurrentState != SERVICE_RUNNING)
6205 WARN("service failed to start %u\n", status.dwCurrentState);
6206 r = ERROR_FUNCTION_FAILED;
6210 done:
6211 uirow = MSI_CreateRecord( 2 );
6212 MSI_RecordSetStringW( uirow, 1, display_name );
6213 MSI_RecordSetStringW( uirow, 2, name );
6214 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6215 msiobj_release( &uirow->hdr );
6217 if (service) CloseServiceHandle(service);
6218 if (scm) CloseServiceHandle(scm);
6220 msi_free(name);
6221 msi_free(args);
6222 msi_free(vector);
6223 msi_free(display_name);
6224 return r;
6227 static UINT ACTION_StartServices( MSIPACKAGE *package )
6229 static const WCHAR query[] = {
6230 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6231 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6232 MSIQUERY *view;
6233 UINT rc;
6235 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6236 if (rc != ERROR_SUCCESS)
6237 return ERROR_SUCCESS;
6239 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6240 msiobj_release(&view->hdr);
6241 return rc;
6244 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6246 DWORD i, needed, count;
6247 ENUM_SERVICE_STATUSW *dependencies;
6248 SERVICE_STATUS ss;
6249 SC_HANDLE depserv;
6250 BOOL stopped, ret = FALSE;
6252 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6253 0, &needed, &count))
6254 return TRUE;
6256 if (GetLastError() != ERROR_MORE_DATA)
6257 return FALSE;
6259 dependencies = msi_alloc(needed);
6260 if (!dependencies)
6261 return FALSE;
6263 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6264 needed, &needed, &count))
6265 goto done;
6267 for (i = 0; i < count; i++)
6269 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6270 SERVICE_STOP | SERVICE_QUERY_STATUS);
6271 if (!depserv)
6272 goto done;
6274 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6275 CloseServiceHandle(depserv);
6276 if (!stopped)
6277 goto done;
6280 ret = TRUE;
6282 done:
6283 msi_free(dependencies);
6284 return ret;
6287 static UINT stop_service( LPCWSTR name )
6289 SC_HANDLE scm = NULL, service = NULL;
6290 SERVICE_STATUS status;
6291 SERVICE_STATUS_PROCESS ssp;
6292 DWORD needed;
6294 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6295 if (!scm)
6297 WARN("Failed to open the SCM: %d\n", GetLastError());
6298 goto done;
6301 service = OpenServiceW(scm, name,
6302 SERVICE_STOP |
6303 SERVICE_QUERY_STATUS |
6304 SERVICE_ENUMERATE_DEPENDENTS);
6305 if (!service)
6307 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6308 goto done;
6311 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6312 sizeof(SERVICE_STATUS_PROCESS), &needed))
6314 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6315 goto done;
6318 if (ssp.dwCurrentState == SERVICE_STOPPED)
6319 goto done;
6321 stop_service_dependents(scm, service);
6323 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6324 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6326 done:
6327 if (service) CloseServiceHandle(service);
6328 if (scm) CloseServiceHandle(scm);
6330 return ERROR_SUCCESS;
6333 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6335 MSIPACKAGE *package = param;
6336 MSICOMPONENT *comp;
6337 MSIRECORD *uirow;
6338 LPCWSTR component;
6339 WCHAR *name, *display_name = NULL;
6340 DWORD event, len;
6341 SC_HANDLE scm;
6343 component = MSI_RecordGetString( rec, 6 );
6344 comp = msi_get_loaded_component( package, component );
6345 if (!comp)
6346 return ERROR_SUCCESS;
6348 event = MSI_RecordGetInteger( rec, 3 );
6349 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6351 comp->Action = msi_get_component_action( package, comp );
6352 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6353 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6355 TRACE("not stopping %s\n", debugstr_w(name));
6356 msi_free( name );
6357 return ERROR_SUCCESS;
6360 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6361 if (!scm)
6363 ERR("Failed to open the service control manager\n");
6364 goto done;
6367 len = 0;
6368 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6369 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6371 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6372 GetServiceDisplayNameW( scm, name, display_name, &len );
6374 CloseServiceHandle( scm );
6376 stop_service( name );
6378 done:
6379 uirow = MSI_CreateRecord( 2 );
6380 MSI_RecordSetStringW( uirow, 1, display_name );
6381 MSI_RecordSetStringW( uirow, 2, name );
6382 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6383 msiobj_release( &uirow->hdr );
6385 msi_free( name );
6386 msi_free( display_name );
6387 return ERROR_SUCCESS;
6390 static UINT ACTION_StopServices( MSIPACKAGE *package )
6392 static const WCHAR query[] = {
6393 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6394 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6395 MSIQUERY *view;
6396 UINT rc;
6398 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6399 if (rc != ERROR_SUCCESS)
6400 return ERROR_SUCCESS;
6402 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6403 msiobj_release(&view->hdr);
6404 return rc;
6407 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6409 MSIPACKAGE *package = param;
6410 MSICOMPONENT *comp;
6411 MSIRECORD *uirow;
6412 LPWSTR name = NULL, display_name = NULL;
6413 DWORD event, len;
6414 SC_HANDLE scm = NULL, service = NULL;
6416 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6417 if (!comp)
6418 return ERROR_SUCCESS;
6420 event = MSI_RecordGetInteger( rec, 3 );
6421 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6423 comp->Action = msi_get_component_action( package, comp );
6424 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6425 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6427 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6428 msi_free( name );
6429 return ERROR_SUCCESS;
6431 stop_service( name );
6433 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6434 if (!scm)
6436 WARN("Failed to open the SCM: %d\n", GetLastError());
6437 goto done;
6440 len = 0;
6441 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6442 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6444 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6445 GetServiceDisplayNameW( scm, name, display_name, &len );
6448 service = OpenServiceW( scm, name, DELETE );
6449 if (!service)
6451 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6452 goto done;
6455 if (!DeleteService( service ))
6456 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6458 done:
6459 uirow = MSI_CreateRecord( 2 );
6460 MSI_RecordSetStringW( uirow, 1, display_name );
6461 MSI_RecordSetStringW( uirow, 2, name );
6462 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6463 msiobj_release( &uirow->hdr );
6465 if (service) CloseServiceHandle( service );
6466 if (scm) CloseServiceHandle( scm );
6467 msi_free( name );
6468 msi_free( display_name );
6470 return ERROR_SUCCESS;
6473 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6475 static const WCHAR query[] = {
6476 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6477 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6478 MSIQUERY *view;
6479 UINT rc;
6481 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6482 if (rc != ERROR_SUCCESS)
6483 return ERROR_SUCCESS;
6485 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6486 msiobj_release( &view->hdr );
6487 return rc;
6490 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6492 MSIPACKAGE *package = param;
6493 LPWSTR driver, driver_path, ptr;
6494 WCHAR outpath[MAX_PATH];
6495 MSIFILE *driver_file = NULL, *setup_file = NULL;
6496 MSICOMPONENT *comp;
6497 MSIRECORD *uirow;
6498 LPCWSTR desc, file_key, component;
6499 DWORD len, usage;
6500 UINT r = ERROR_SUCCESS;
6502 static const WCHAR driver_fmt[] = {
6503 'D','r','i','v','e','r','=','%','s',0};
6504 static const WCHAR setup_fmt[] = {
6505 'S','e','t','u','p','=','%','s',0};
6506 static const WCHAR usage_fmt[] = {
6507 'F','i','l','e','U','s','a','g','e','=','1',0};
6509 component = MSI_RecordGetString( rec, 2 );
6510 comp = msi_get_loaded_component( package, component );
6511 if (!comp)
6512 return ERROR_SUCCESS;
6514 comp->Action = msi_get_component_action( package, comp );
6515 if (comp->Action != INSTALLSTATE_LOCAL)
6517 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6518 return ERROR_SUCCESS;
6520 desc = MSI_RecordGetString(rec, 3);
6522 file_key = MSI_RecordGetString( rec, 4 );
6523 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6525 file_key = MSI_RecordGetString( rec, 5 );
6526 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6528 if (!driver_file)
6530 ERR("ODBC Driver entry not found!\n");
6531 return ERROR_FUNCTION_FAILED;
6534 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6535 if (setup_file)
6536 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6537 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6539 driver = msi_alloc(len * sizeof(WCHAR));
6540 if (!driver)
6541 return ERROR_OUTOFMEMORY;
6543 ptr = driver;
6544 lstrcpyW(ptr, desc);
6545 ptr += lstrlenW(ptr) + 1;
6547 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6548 ptr += len + 1;
6550 if (setup_file)
6552 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6553 ptr += len + 1;
6556 lstrcpyW(ptr, usage_fmt);
6557 ptr += lstrlenW(ptr) + 1;
6558 *ptr = '\0';
6560 if (!driver_file->TargetPath)
6562 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6563 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6565 driver_path = strdupW(driver_file->TargetPath);
6566 ptr = strrchrW(driver_path, '\\');
6567 if (ptr) *ptr = '\0';
6569 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6570 NULL, ODBC_INSTALL_COMPLETE, &usage))
6572 ERR("Failed to install SQL driver!\n");
6573 r = ERROR_FUNCTION_FAILED;
6576 uirow = MSI_CreateRecord( 5 );
6577 MSI_RecordSetStringW( uirow, 1, desc );
6578 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6579 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6580 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6581 msiobj_release( &uirow->hdr );
6583 msi_free(driver);
6584 msi_free(driver_path);
6586 return r;
6589 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6591 MSIPACKAGE *package = param;
6592 LPWSTR translator, translator_path, ptr;
6593 WCHAR outpath[MAX_PATH];
6594 MSIFILE *translator_file = NULL, *setup_file = NULL;
6595 MSICOMPONENT *comp;
6596 MSIRECORD *uirow;
6597 LPCWSTR desc, file_key, component;
6598 DWORD len, usage;
6599 UINT r = ERROR_SUCCESS;
6601 static const WCHAR translator_fmt[] = {
6602 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6603 static const WCHAR setup_fmt[] = {
6604 'S','e','t','u','p','=','%','s',0};
6606 component = MSI_RecordGetString( rec, 2 );
6607 comp = msi_get_loaded_component( package, component );
6608 if (!comp)
6609 return ERROR_SUCCESS;
6611 comp->Action = msi_get_component_action( package, comp );
6612 if (comp->Action != INSTALLSTATE_LOCAL)
6614 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6615 return ERROR_SUCCESS;
6617 desc = MSI_RecordGetString(rec, 3);
6619 file_key = MSI_RecordGetString( rec, 4 );
6620 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6622 file_key = MSI_RecordGetString( rec, 5 );
6623 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6625 if (!translator_file)
6627 ERR("ODBC Translator entry not found!\n");
6628 return ERROR_FUNCTION_FAILED;
6631 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6632 if (setup_file)
6633 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6635 translator = msi_alloc(len * sizeof(WCHAR));
6636 if (!translator)
6637 return ERROR_OUTOFMEMORY;
6639 ptr = translator;
6640 lstrcpyW(ptr, desc);
6641 ptr += lstrlenW(ptr) + 1;
6643 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6644 ptr += len + 1;
6646 if (setup_file)
6648 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6649 ptr += len + 1;
6651 *ptr = '\0';
6653 translator_path = strdupW(translator_file->TargetPath);
6654 ptr = strrchrW(translator_path, '\\');
6655 if (ptr) *ptr = '\0';
6657 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6658 NULL, ODBC_INSTALL_COMPLETE, &usage))
6660 ERR("Failed to install SQL translator!\n");
6661 r = ERROR_FUNCTION_FAILED;
6664 uirow = MSI_CreateRecord( 5 );
6665 MSI_RecordSetStringW( uirow, 1, desc );
6666 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6667 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6668 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6669 msiobj_release( &uirow->hdr );
6671 msi_free(translator);
6672 msi_free(translator_path);
6674 return r;
6677 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6679 MSIPACKAGE *package = param;
6680 MSICOMPONENT *comp;
6681 LPWSTR attrs;
6682 LPCWSTR desc, driver, component;
6683 WORD request = ODBC_ADD_SYS_DSN;
6684 INT registration;
6685 DWORD len;
6686 UINT r = ERROR_SUCCESS;
6687 MSIRECORD *uirow;
6689 static const WCHAR attrs_fmt[] = {
6690 'D','S','N','=','%','s',0 };
6692 component = MSI_RecordGetString( rec, 2 );
6693 comp = msi_get_loaded_component( package, component );
6694 if (!comp)
6695 return ERROR_SUCCESS;
6697 comp->Action = msi_get_component_action( package, comp );
6698 if (comp->Action != INSTALLSTATE_LOCAL)
6700 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6701 return ERROR_SUCCESS;
6704 desc = MSI_RecordGetString(rec, 3);
6705 driver = MSI_RecordGetString(rec, 4);
6706 registration = MSI_RecordGetInteger(rec, 5);
6708 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6709 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6711 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6712 attrs = msi_alloc(len * sizeof(WCHAR));
6713 if (!attrs)
6714 return ERROR_OUTOFMEMORY;
6716 len = sprintfW(attrs, attrs_fmt, desc);
6717 attrs[len + 1] = 0;
6719 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6721 ERR("Failed to install SQL data source!\n");
6722 r = ERROR_FUNCTION_FAILED;
6725 uirow = MSI_CreateRecord( 5 );
6726 MSI_RecordSetStringW( uirow, 1, desc );
6727 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6728 MSI_RecordSetInteger( uirow, 3, request );
6729 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6730 msiobj_release( &uirow->hdr );
6732 msi_free(attrs);
6734 return r;
6737 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6739 static const WCHAR driver_query[] = {
6740 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6741 'O','D','B','C','D','r','i','v','e','r',0};
6742 static const WCHAR translator_query[] = {
6743 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6744 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6745 static const WCHAR source_query[] = {
6746 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6747 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6748 MSIQUERY *view;
6749 UINT rc;
6751 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6752 if (rc == ERROR_SUCCESS)
6754 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6755 msiobj_release(&view->hdr);
6756 if (rc != ERROR_SUCCESS)
6757 return rc;
6759 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6760 if (rc == ERROR_SUCCESS)
6762 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6763 msiobj_release(&view->hdr);
6764 if (rc != ERROR_SUCCESS)
6765 return rc;
6767 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6768 if (rc == ERROR_SUCCESS)
6770 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6771 msiobj_release(&view->hdr);
6772 if (rc != ERROR_SUCCESS)
6773 return rc;
6775 return ERROR_SUCCESS;
6778 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6780 MSIPACKAGE *package = param;
6781 MSICOMPONENT *comp;
6782 MSIRECORD *uirow;
6783 DWORD usage;
6784 LPCWSTR desc, component;
6786 component = MSI_RecordGetString( rec, 2 );
6787 comp = msi_get_loaded_component( package, component );
6788 if (!comp)
6789 return ERROR_SUCCESS;
6791 comp->Action = msi_get_component_action( package, comp );
6792 if (comp->Action != INSTALLSTATE_ABSENT)
6794 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6795 return ERROR_SUCCESS;
6798 desc = MSI_RecordGetString( rec, 3 );
6799 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6801 WARN("Failed to remove ODBC driver\n");
6803 else if (!usage)
6805 FIXME("Usage count reached 0\n");
6808 uirow = MSI_CreateRecord( 2 );
6809 MSI_RecordSetStringW( uirow, 1, desc );
6810 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6811 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6812 msiobj_release( &uirow->hdr );
6814 return ERROR_SUCCESS;
6817 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6819 MSIPACKAGE *package = param;
6820 MSICOMPONENT *comp;
6821 MSIRECORD *uirow;
6822 DWORD usage;
6823 LPCWSTR desc, component;
6825 component = MSI_RecordGetString( rec, 2 );
6826 comp = msi_get_loaded_component( package, component );
6827 if (!comp)
6828 return ERROR_SUCCESS;
6830 comp->Action = msi_get_component_action( package, comp );
6831 if (comp->Action != INSTALLSTATE_ABSENT)
6833 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6834 return ERROR_SUCCESS;
6837 desc = MSI_RecordGetString( rec, 3 );
6838 if (!SQLRemoveTranslatorW( desc, &usage ))
6840 WARN("Failed to remove ODBC translator\n");
6842 else if (!usage)
6844 FIXME("Usage count reached 0\n");
6847 uirow = MSI_CreateRecord( 2 );
6848 MSI_RecordSetStringW( uirow, 1, desc );
6849 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6850 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6851 msiobj_release( &uirow->hdr );
6853 return ERROR_SUCCESS;
6856 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6858 MSIPACKAGE *package = param;
6859 MSICOMPONENT *comp;
6860 MSIRECORD *uirow;
6861 LPWSTR attrs;
6862 LPCWSTR desc, driver, component;
6863 WORD request = ODBC_REMOVE_SYS_DSN;
6864 INT registration;
6865 DWORD len;
6867 static const WCHAR attrs_fmt[] = {
6868 'D','S','N','=','%','s',0 };
6870 component = MSI_RecordGetString( rec, 2 );
6871 comp = msi_get_loaded_component( package, component );
6872 if (!comp)
6873 return ERROR_SUCCESS;
6875 comp->Action = msi_get_component_action( package, comp );
6876 if (comp->Action != INSTALLSTATE_ABSENT)
6878 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6879 return ERROR_SUCCESS;
6882 desc = MSI_RecordGetString( rec, 3 );
6883 driver = MSI_RecordGetString( rec, 4 );
6884 registration = MSI_RecordGetInteger( rec, 5 );
6886 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6887 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6889 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6890 attrs = msi_alloc( len * sizeof(WCHAR) );
6891 if (!attrs)
6892 return ERROR_OUTOFMEMORY;
6894 FIXME("Use ODBCSourceAttribute table\n");
6896 len = sprintfW( attrs, attrs_fmt, desc );
6897 attrs[len + 1] = 0;
6899 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6901 WARN("Failed to remove ODBC data source\n");
6903 msi_free( attrs );
6905 uirow = MSI_CreateRecord( 3 );
6906 MSI_RecordSetStringW( uirow, 1, desc );
6907 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6908 MSI_RecordSetInteger( uirow, 3, request );
6909 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6910 msiobj_release( &uirow->hdr );
6912 return ERROR_SUCCESS;
6915 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6917 static const WCHAR driver_query[] = {
6918 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6919 'O','D','B','C','D','r','i','v','e','r',0};
6920 static const WCHAR translator_query[] = {
6921 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6922 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6923 static const WCHAR source_query[] = {
6924 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6925 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6926 MSIQUERY *view;
6927 UINT rc;
6929 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6930 if (rc == ERROR_SUCCESS)
6932 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6933 msiobj_release( &view->hdr );
6934 if (rc != ERROR_SUCCESS)
6935 return rc;
6937 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6938 if (rc == ERROR_SUCCESS)
6940 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6941 msiobj_release( &view->hdr );
6942 if (rc != ERROR_SUCCESS)
6943 return rc;
6945 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6946 if (rc == ERROR_SUCCESS)
6948 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6949 msiobj_release( &view->hdr );
6950 if (rc != ERROR_SUCCESS)
6951 return rc;
6953 return ERROR_SUCCESS;
6956 #define ENV_ACT_SETALWAYS 0x1
6957 #define ENV_ACT_SETABSENT 0x2
6958 #define ENV_ACT_REMOVE 0x4
6959 #define ENV_ACT_REMOVEMATCH 0x8
6961 #define ENV_MOD_MACHINE 0x20000000
6962 #define ENV_MOD_APPEND 0x40000000
6963 #define ENV_MOD_PREFIX 0x80000000
6964 #define ENV_MOD_MASK 0xC0000000
6966 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6968 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6970 LPCWSTR cptr = *name;
6972 static const WCHAR prefix[] = {'[','~',']',0};
6973 static const int prefix_len = 3;
6975 *flags = 0;
6976 while (*cptr)
6978 if (*cptr == '=')
6979 *flags |= ENV_ACT_SETALWAYS;
6980 else if (*cptr == '+')
6981 *flags |= ENV_ACT_SETABSENT;
6982 else if (*cptr == '-')
6983 *flags |= ENV_ACT_REMOVE;
6984 else if (*cptr == '!')
6985 *flags |= ENV_ACT_REMOVEMATCH;
6986 else if (*cptr == '*')
6987 *flags |= ENV_MOD_MACHINE;
6988 else
6989 break;
6991 cptr++;
6992 (*name)++;
6995 if (!*cptr)
6997 ERR("Missing environment variable\n");
6998 return ERROR_FUNCTION_FAILED;
7001 if (*value)
7003 LPCWSTR ptr = *value;
7004 if (!strncmpW(ptr, prefix, prefix_len))
7006 if (ptr[prefix_len] == szSemiColon[0])
7008 *flags |= ENV_MOD_APPEND;
7009 *value += lstrlenW(prefix);
7011 else
7013 *value = NULL;
7016 else if (lstrlenW(*value) >= prefix_len)
7018 ptr += lstrlenW(ptr) - prefix_len;
7019 if (!strcmpW( ptr, prefix ))
7021 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7023 *flags |= ENV_MOD_PREFIX;
7024 /* the "[~]" will be removed by deformat_string */;
7026 else
7028 *value = NULL;
7034 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7035 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7036 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7037 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7039 ERR("Invalid flags: %08x\n", *flags);
7040 return ERROR_FUNCTION_FAILED;
7043 if (!*flags)
7044 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7046 return ERROR_SUCCESS;
7049 static UINT open_env_key( DWORD flags, HKEY *key )
7051 static const WCHAR user_env[] =
7052 {'E','n','v','i','r','o','n','m','e','n','t',0};
7053 static const WCHAR machine_env[] =
7054 {'S','y','s','t','e','m','\\',
7055 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7056 'C','o','n','t','r','o','l','\\',
7057 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7058 'E','n','v','i','r','o','n','m','e','n','t',0};
7059 const WCHAR *env;
7060 HKEY root;
7061 LONG res;
7063 if (flags & ENV_MOD_MACHINE)
7065 env = machine_env;
7066 root = HKEY_LOCAL_MACHINE;
7068 else
7070 env = user_env;
7071 root = HKEY_CURRENT_USER;
7074 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7075 if (res != ERROR_SUCCESS)
7077 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7078 return ERROR_FUNCTION_FAILED;
7081 return ERROR_SUCCESS;
7084 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7086 MSIPACKAGE *package = param;
7087 LPCWSTR name, value, component;
7088 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
7089 DWORD flags, type, size, len, len_value = 0;
7090 UINT res;
7091 HKEY env = NULL;
7092 MSICOMPONENT *comp;
7093 MSIRECORD *uirow;
7094 int action = 0, found = 0;
7096 component = MSI_RecordGetString(rec, 4);
7097 comp = msi_get_loaded_component(package, component);
7098 if (!comp)
7099 return ERROR_SUCCESS;
7101 comp->Action = msi_get_component_action( package, comp );
7102 if (comp->Action != INSTALLSTATE_LOCAL)
7104 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7105 return ERROR_SUCCESS;
7107 name = MSI_RecordGetString(rec, 2);
7108 value = MSI_RecordGetString(rec, 3);
7110 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7112 res = env_parse_flags(&name, &value, &flags);
7113 if (res != ERROR_SUCCESS || !value)
7114 goto done;
7116 if (value && !deformat_string(package, value, &deformatted))
7118 res = ERROR_OUTOFMEMORY;
7119 goto done;
7122 if ((value = deformatted))
7124 if (flags & ENV_MOD_PREFIX)
7126 p = strrchrW( value, ';' );
7127 len_value = p - value;
7129 else if (flags & ENV_MOD_APPEND)
7131 value = strchrW( value, ';' ) + 1;
7132 len_value = strlenW( value );
7134 else len_value = strlenW( value );
7137 res = open_env_key( flags, &env );
7138 if (res != ERROR_SUCCESS)
7139 goto done;
7141 if (flags & ENV_MOD_MACHINE)
7142 action |= 0x20000000;
7144 size = 0;
7145 type = REG_SZ;
7146 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7147 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7148 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7149 goto done;
7151 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7153 action = 0x2;
7155 /* Nothing to do. */
7156 if (!value)
7158 res = ERROR_SUCCESS;
7159 goto done;
7161 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7162 newval = strdupW(value);
7163 if (!newval)
7165 res = ERROR_OUTOFMEMORY;
7166 goto done;
7169 else
7171 action = 0x1;
7173 /* Contrary to MSDN, +-variable to [~];path works */
7174 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7176 res = ERROR_SUCCESS;
7177 goto done;
7180 if (!(p = q = data = msi_alloc( size )))
7182 msi_free(deformatted);
7183 RegCloseKey(env);
7184 return ERROR_OUTOFMEMORY;
7187 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7188 if (res != ERROR_SUCCESS)
7189 goto done;
7191 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7193 action = 0x4;
7194 res = RegDeleteValueW(env, name);
7195 if (res != ERROR_SUCCESS)
7196 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7197 goto done;
7200 for (;;)
7202 while (*q && *q != ';') q++;
7203 len = q - p;
7204 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7205 (!p[len] || p[len] == ';'))
7207 found = 1;
7208 break;
7210 if (!*q) break;
7211 p = ++q;
7214 if (found)
7216 TRACE("string already set\n");
7217 goto done;
7220 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7221 if (!(p = newval = msi_alloc( size )))
7223 res = ERROR_OUTOFMEMORY;
7224 goto done;
7227 if (flags & ENV_MOD_PREFIX)
7229 memcpy( newval, value, len_value * sizeof(WCHAR) );
7230 newval[len_value] = ';';
7231 p = newval + len_value + 1;
7232 action |= 0x80000000;
7235 strcpyW( p, data );
7237 if (flags & ENV_MOD_APPEND)
7239 p += strlenW( data );
7240 *p++ = ';';
7241 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7242 action |= 0x40000000;
7245 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7246 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7247 if (res)
7249 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7252 done:
7253 uirow = MSI_CreateRecord( 3 );
7254 MSI_RecordSetStringW( uirow, 1, name );
7255 MSI_RecordSetStringW( uirow, 2, newval );
7256 MSI_RecordSetInteger( uirow, 3, action );
7257 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7258 msiobj_release( &uirow->hdr );
7260 if (env) RegCloseKey(env);
7261 msi_free(deformatted);
7262 msi_free(data);
7263 msi_free(newval);
7264 return res;
7267 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7269 static const WCHAR query[] = {
7270 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7271 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7272 MSIQUERY *view;
7273 UINT rc;
7275 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7276 if (rc != ERROR_SUCCESS)
7277 return ERROR_SUCCESS;
7279 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7280 msiobj_release(&view->hdr);
7281 return rc;
7284 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7286 MSIPACKAGE *package = param;
7287 LPCWSTR name, value, component;
7288 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7289 DWORD flags, type, size, len, len_value = 0, len_new_value;
7290 HKEY env;
7291 MSICOMPONENT *comp;
7292 MSIRECORD *uirow;
7293 int action = 0;
7294 LONG res;
7295 UINT r;
7297 component = MSI_RecordGetString( rec, 4 );
7298 comp = msi_get_loaded_component( package, component );
7299 if (!comp)
7300 return ERROR_SUCCESS;
7302 comp->Action = msi_get_component_action( package, comp );
7303 if (comp->Action != INSTALLSTATE_ABSENT)
7305 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7306 return ERROR_SUCCESS;
7308 name = MSI_RecordGetString( rec, 2 );
7309 value = MSI_RecordGetString( rec, 3 );
7311 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7313 r = env_parse_flags( &name, &value, &flags );
7314 if (r != ERROR_SUCCESS)
7315 return r;
7317 if (!(flags & ENV_ACT_REMOVE))
7319 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7320 return ERROR_SUCCESS;
7323 if (value && !deformat_string( package, value, &deformatted ))
7324 return ERROR_OUTOFMEMORY;
7326 if ((value = deformatted))
7328 if (flags & ENV_MOD_PREFIX)
7330 p = strchrW( value, ';' );
7331 len_value = p - value;
7333 else if (flags & ENV_MOD_APPEND)
7335 value = strchrW( value, ';' ) + 1;
7336 len_value = strlenW( value );
7338 else len_value = strlenW( value );
7341 r = open_env_key( flags, &env );
7342 if (r != ERROR_SUCCESS)
7344 r = ERROR_SUCCESS;
7345 goto done;
7348 if (flags & ENV_MOD_MACHINE)
7349 action |= 0x20000000;
7351 size = 0;
7352 type = REG_SZ;
7353 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7354 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7355 goto done;
7357 if (!(new_value = msi_alloc( size ))) goto done;
7359 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7360 if (res != ERROR_SUCCESS)
7361 goto done;
7363 len_new_value = size / sizeof(WCHAR) - 1;
7364 p = q = new_value;
7365 for (;;)
7367 while (*q && *q != ';') q++;
7368 len = q - p;
7369 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7371 if (*q == ';') q++;
7372 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7373 break;
7375 if (!*q) break;
7376 p = ++q;
7379 if (!new_value[0] || !value)
7381 TRACE("removing %s\n", debugstr_w(name));
7382 res = RegDeleteValueW( env, name );
7383 if (res != ERROR_SUCCESS)
7384 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7386 else
7388 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7389 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7390 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7391 if (res != ERROR_SUCCESS)
7392 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7395 done:
7396 uirow = MSI_CreateRecord( 3 );
7397 MSI_RecordSetStringW( uirow, 1, name );
7398 MSI_RecordSetStringW( uirow, 2, value );
7399 MSI_RecordSetInteger( uirow, 3, action );
7400 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7401 msiobj_release( &uirow->hdr );
7403 if (env) RegCloseKey( env );
7404 msi_free( deformatted );
7405 msi_free( new_value );
7406 return r;
7409 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7411 static const WCHAR query[] = {
7412 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7413 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7414 MSIQUERY *view;
7415 UINT rc;
7417 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7418 if (rc != ERROR_SUCCESS)
7419 return ERROR_SUCCESS;
7421 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7422 msiobj_release( &view->hdr );
7423 return rc;
7426 UINT msi_validate_product_id( MSIPACKAGE *package )
7428 LPWSTR key, template, id;
7429 UINT r = ERROR_SUCCESS;
7431 id = msi_dup_property( package->db, szProductID );
7432 if (id)
7434 msi_free( id );
7435 return ERROR_SUCCESS;
7437 template = msi_dup_property( package->db, szPIDTemplate );
7438 key = msi_dup_property( package->db, szPIDKEY );
7439 if (key && template)
7441 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7442 r = msi_set_property( package->db, szProductID, key, -1 );
7444 msi_free( template );
7445 msi_free( key );
7446 return r;
7449 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7451 return msi_validate_product_id( package );
7454 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7456 TRACE("\n");
7457 package->need_reboot_at_end = 1;
7458 return ERROR_SUCCESS;
7461 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7463 static const WCHAR szAvailableFreeReg[] =
7464 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7465 MSIRECORD *uirow;
7466 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7468 TRACE("%p %d kilobytes\n", package, space);
7470 uirow = MSI_CreateRecord( 1 );
7471 MSI_RecordSetInteger( uirow, 1, space );
7472 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7473 msiobj_release( &uirow->hdr );
7475 return ERROR_SUCCESS;
7478 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7480 TRACE("%p\n", package);
7482 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7483 return ERROR_SUCCESS;
7486 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7488 FIXME("%p\n", package);
7489 return ERROR_SUCCESS;
7492 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7494 static const WCHAR driver_query[] = {
7495 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7496 'O','D','B','C','D','r','i','v','e','r',0};
7497 static const WCHAR translator_query[] = {
7498 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7499 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7500 MSIQUERY *view;
7501 UINT r, count;
7503 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7504 if (r == ERROR_SUCCESS)
7506 count = 0;
7507 r = MSI_IterateRecords( view, &count, NULL, package );
7508 msiobj_release( &view->hdr );
7509 if (r != ERROR_SUCCESS)
7510 return r;
7511 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7513 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7514 if (r == ERROR_SUCCESS)
7516 count = 0;
7517 r = MSI_IterateRecords( view, &count, NULL, package );
7518 msiobj_release( &view->hdr );
7519 if (r != ERROR_SUCCESS)
7520 return r;
7521 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7523 return ERROR_SUCCESS;
7526 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7528 static const WCHAR fmtW[] =
7529 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7530 'R','E','M','O','V','E','=','%','s',0};
7531 MSIPACKAGE *package = param;
7532 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7533 int attrs = MSI_RecordGetInteger( rec, 5 );
7534 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7535 WCHAR *product, *features, *cmd;
7536 STARTUPINFOW si;
7537 PROCESS_INFORMATION info;
7538 BOOL ret;
7540 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7541 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7543 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7545 len += strlenW( product );
7546 if (features)
7547 len += strlenW( features );
7548 else
7549 len += sizeof(szAll) / sizeof(szAll[0]);
7551 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7553 msi_free( product );
7554 msi_free( features );
7555 return ERROR_OUTOFMEMORY;
7557 sprintfW( cmd, fmtW, product, features ? features : szAll );
7558 msi_free( product );
7559 msi_free( features );
7561 memset( &si, 0, sizeof(STARTUPINFOW) );
7562 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7563 msi_free( cmd );
7564 if (!ret) return GetLastError();
7565 CloseHandle( info.hThread );
7567 WaitForSingleObject( info.hProcess, INFINITE );
7568 CloseHandle( info.hProcess );
7569 return ERROR_SUCCESS;
7572 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7574 static const WCHAR query[] = {
7575 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7576 MSIQUERY *view;
7577 UINT r;
7579 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7580 if (r == ERROR_SUCCESS)
7582 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7583 msiobj_release( &view->hdr );
7584 if (r != ERROR_SUCCESS)
7585 return r;
7587 return ERROR_SUCCESS;
7590 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7592 MSIPACKAGE *package = param;
7593 int attributes = MSI_RecordGetInteger( rec, 5 );
7595 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7597 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7598 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7599 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7600 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7601 HKEY hkey;
7602 UINT r;
7604 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7606 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7607 if (r != ERROR_SUCCESS)
7608 return ERROR_SUCCESS;
7610 else
7612 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7613 if (r != ERROR_SUCCESS)
7614 return ERROR_SUCCESS;
7616 RegCloseKey( hkey );
7618 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7619 debugstr_w(upgrade_code), debugstr_w(version_min),
7620 debugstr_w(version_max), debugstr_w(language));
7622 return ERROR_SUCCESS;
7625 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7627 static const WCHAR query[] = {
7628 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7629 'U','p','g','r','a','d','e',0};
7630 MSIQUERY *view;
7631 UINT r;
7633 if (msi_get_property_int( package->db, szInstalled, 0 ))
7635 TRACE("product is installed, skipping action\n");
7636 return ERROR_SUCCESS;
7638 if (msi_get_property_int( package->db, szPreselected, 0 ))
7640 TRACE("Preselected property is set, not migrating feature states\n");
7641 return ERROR_SUCCESS;
7643 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7644 if (r == ERROR_SUCCESS)
7646 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7647 msiobj_release( &view->hdr );
7648 if (r != ERROR_SUCCESS)
7649 return r;
7651 return ERROR_SUCCESS;
7654 static void bind_image( const char *filename, const char *path )
7656 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7658 WARN("failed to bind image %u\n", GetLastError());
7662 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7664 UINT i;
7665 MSIFILE *file;
7666 MSIPACKAGE *package = param;
7667 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7668 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7669 char *filenameA, *pathA;
7670 WCHAR *pathW, **path_list;
7672 if (!(file = msi_get_loaded_file( package, key )))
7674 WARN("file %s not found\n", debugstr_w(key));
7675 return ERROR_SUCCESS;
7677 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7678 path_list = msi_split_string( paths, ';' );
7679 if (!path_list) bind_image( filenameA, NULL );
7680 else
7682 for (i = 0; path_list[i] && path_list[i][0]; i++)
7684 deformat_string( package, path_list[i], &pathW );
7685 if ((pathA = strdupWtoA( pathW )))
7687 bind_image( filenameA, pathA );
7688 msi_free( pathA );
7690 msi_free( pathW );
7693 msi_free( path_list );
7694 msi_free( filenameA );
7695 return ERROR_SUCCESS;
7698 static UINT ACTION_BindImage( MSIPACKAGE *package )
7700 static const WCHAR query[] = {
7701 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7702 'B','i','n','d','I','m','a','g','e',0};
7703 MSIQUERY *view;
7704 UINT r;
7706 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7707 if (r == ERROR_SUCCESS)
7709 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7710 msiobj_release( &view->hdr );
7711 if (r != ERROR_SUCCESS)
7712 return r;
7714 return ERROR_SUCCESS;
7717 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7719 static const WCHAR query[] = {
7720 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7721 MSIQUERY *view;
7722 DWORD count = 0;
7723 UINT r;
7725 r = MSI_OpenQuery( package->db, &view, query, table );
7726 if (r == ERROR_SUCCESS)
7728 r = MSI_IterateRecords(view, &count, NULL, package);
7729 msiobj_release(&view->hdr);
7730 if (r != ERROR_SUCCESS)
7731 return r;
7733 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7734 return ERROR_SUCCESS;
7737 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7739 static const WCHAR table[] = {
7740 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7741 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7744 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7746 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7747 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7750 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7752 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7753 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7756 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7758 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7759 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7762 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7764 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7765 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7768 static const struct
7770 const WCHAR *action;
7771 const UINT description;
7772 const UINT template;
7773 UINT (*handler)(MSIPACKAGE *);
7774 const WCHAR *action_rollback;
7776 StandardActions[] =
7778 { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7779 { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7780 { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7781 { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7782 { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7783 { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7784 { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
7785 { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
7786 { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
7787 { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
7788 { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7789 { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
7790 { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7791 { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7792 { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
7793 { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7794 { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
7795 { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
7796 { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
7797 { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
7798 { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
7799 { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
7800 { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
7801 { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7802 { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7803 { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
7804 { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7805 { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7806 { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7807 { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7808 { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7809 { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7810 { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
7811 { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
7812 { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
7813 { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
7814 { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7815 { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
7816 { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7817 { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
7818 { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7819 { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7820 { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7821 { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7822 { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7823 { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7824 { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7825 { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7826 { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
7827 { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
7828 { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
7829 { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
7830 { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7831 { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
7832 { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
7833 { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7834 { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
7835 { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
7836 { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
7837 { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7838 { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
7839 { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
7840 { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
7841 { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
7842 { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7843 { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7844 { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
7845 { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7846 { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
7847 { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7848 { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7849 { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7850 { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
7851 { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7852 { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
7853 { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7854 { szINSTALL, 0, 0, ACTION_INSTALL, NULL },
7855 { 0 }
7858 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
7860 UINT rc = ERROR_FUNCTION_NOT_CALLED;
7861 UINT i;
7863 i = 0;
7864 while (StandardActions[i].action != NULL)
7866 if (!strcmpW( StandardActions[i].action, action ))
7868 WCHAR description[100] = {0}, template[100] = {0};
7870 if (StandardActions[i].description != 0)
7871 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
7872 if (StandardActions[i].template != 0)
7873 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
7875 ui_actionstart(package, action, description, template);
7876 if (StandardActions[i].handler)
7878 ui_actioninfo( package, action, TRUE, 0 );
7879 rc = StandardActions[i].handler( package );
7880 ui_actioninfo( package, action, FALSE, !rc );
7882 if (StandardActions[i].action_rollback && !package->need_rollback)
7884 TRACE("scheduling rollback action\n");
7885 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7888 else
7890 FIXME("unhandled standard action %s\n", debugstr_w(action));
7891 rc = ERROR_SUCCESS;
7893 break;
7895 i++;
7897 return rc;
7900 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
7902 UINT rc;
7904 TRACE("Performing action (%s)\n", debugstr_w(action));
7906 package->action_progress_increment = 0;
7907 rc = ACTION_HandleStandardAction(package, action);
7909 if (rc == ERROR_FUNCTION_NOT_CALLED)
7910 rc = ACTION_HandleCustomAction(package, action);
7912 if (rc == ERROR_FUNCTION_NOT_CALLED)
7913 WARN("unhandled msi action %s\n", debugstr_w(action));
7915 return rc;
7918 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7920 UINT rc = ERROR_SUCCESS;
7921 MSIRECORD *row;
7923 static const WCHAR query[] =
7924 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7925 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7926 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7927 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7928 static const WCHAR ui_query[] =
7929 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7930 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7931 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7932 ' ', '=',' ','%','i',0};
7934 if (needs_ui_sequence(package))
7935 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7936 else
7937 row = MSI_QueryGetRecord(package->db, query, seq);
7939 if (row)
7941 LPCWSTR action, cond;
7943 TRACE("Running the actions\n");
7945 /* check conditions */
7946 cond = MSI_RecordGetString(row, 2);
7948 /* this is a hack to skip errors in the condition code */
7949 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7951 msiobj_release(&row->hdr);
7952 return ERROR_SUCCESS;
7955 action = MSI_RecordGetString(row, 1);
7956 if (!action)
7958 ERR("failed to fetch action\n");
7959 msiobj_release(&row->hdr);
7960 return ERROR_FUNCTION_FAILED;
7963 rc = ACTION_PerformAction(package, action);
7965 msiobj_release(&row->hdr);
7968 return rc;
7971 /****************************************************
7972 * TOP level entry points
7973 *****************************************************/
7975 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7976 LPCWSTR szCommandLine )
7978 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7979 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7980 WCHAR *reinstall = NULL, *productcode, *action;
7981 UINT rc;
7982 DWORD len = 0;
7984 if (szPackagePath)
7986 LPWSTR p, dir;
7987 LPCWSTR file;
7989 dir = strdupW(szPackagePath);
7990 p = strrchrW(dir, '\\');
7991 if (p)
7993 *(++p) = 0;
7994 file = szPackagePath + (p - dir);
7996 else
7998 msi_free(dir);
7999 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
8000 GetCurrentDirectoryW(MAX_PATH, dir);
8001 lstrcatW(dir, szBackSlash);
8002 file = szPackagePath;
8005 msi_free( package->PackagePath );
8006 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8007 if (!package->PackagePath)
8009 msi_free(dir);
8010 return ERROR_OUTOFMEMORY;
8013 lstrcpyW(package->PackagePath, dir);
8014 lstrcatW(package->PackagePath, file);
8015 msi_free(dir);
8017 msi_set_sourcedir_props(package, FALSE);
8020 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8021 if (rc != ERROR_SUCCESS)
8022 return rc;
8024 msi_apply_transforms( package );
8025 msi_apply_patches( package );
8027 if (msi_get_property( package->db, szAction, NULL, &len ))
8028 msi_set_property( package->db, szAction, szINSTALL, -1 );
8029 action = msi_dup_property( package->db, szAction );
8030 CharUpperW(action);
8032 msi_set_original_database_property( package->db, szPackagePath );
8033 msi_parse_command_line( package, szCommandLine, FALSE );
8034 msi_adjust_privilege_properties( package );
8035 msi_set_context( package );
8037 productcode = msi_dup_property( package->db, szProductCode );
8038 if (strcmpiW( productcode, package->ProductCode ))
8040 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
8041 msi_free( package->ProductCode );
8042 package->ProductCode = productcode;
8044 else msi_free( productcode );
8046 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
8048 TRACE("disabling rollback\n");
8049 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8052 rc = ACTION_PerformAction(package, action);
8054 /* process the ending type action */
8055 if (rc == ERROR_SUCCESS)
8056 ACTION_PerformActionSequence(package, -1);
8057 else if (rc == ERROR_INSTALL_USEREXIT)
8058 ACTION_PerformActionSequence(package, -2);
8059 else if (rc == ERROR_INSTALL_SUSPEND)
8060 ACTION_PerformActionSequence(package, -4);
8061 else /* failed */
8063 ACTION_PerformActionSequence(package, -3);
8064 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8066 package->need_rollback = TRUE;
8070 /* finish up running custom actions */
8071 ACTION_FinishCustomActions(package);
8073 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
8075 WARN("installation failed, running rollback script\n");
8076 execute_script( package, SCRIPT_ROLLBACK );
8078 msi_free( reinstall );
8079 msi_free( action );
8081 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8082 return ERROR_SUCCESS_REBOOT_REQUIRED;
8084 return rc;