schedsvc: Fix xml buffer leak (Valgrind).
[wine.git] / dlls / msi / action.c
blob4d7e8b99fa0a3868928949e163c7f2f5aa793c12
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 "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'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};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnpublishProduct[] =
149 {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
150 static const WCHAR szUnregisterComPlus[] =
151 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
152 static const WCHAR szUnregisterTypeLibraries[] =
153 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
154 static const WCHAR szValidateProductID[] =
155 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
156 static const WCHAR szWriteEnvironmentStrings[] =
157 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
159 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
161 static const WCHAR Query_t[] =
162 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
163 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
164 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
165 ' ','\'','%','s','\'',0};
166 MSIRECORD * row;
168 row = MSI_QueryGetRecord( package->db, Query_t, action );
169 if (!row)
170 return;
171 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
172 msiobj_release(&row->hdr);
175 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
176 UINT rc)
178 MSIRECORD * row;
179 static const WCHAR template_s[]=
180 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
181 '%','s', '.',0};
182 static const WCHAR template_e[]=
183 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
184 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
185 '%','i','.',0};
186 static const WCHAR format[] =
187 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
188 WCHAR message[1024];
189 WCHAR timet[0x100];
191 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
192 if (start)
193 sprintfW(message,template_s,timet,action);
194 else
195 sprintfW(message,template_e,timet,action,rc);
197 row = MSI_CreateRecord(1);
198 MSI_RecordSetStringW(row,1,message);
200 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
201 msiobj_release(&row->hdr);
204 enum parse_state
206 state_whitespace,
207 state_token,
208 state_quote
211 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
213 enum parse_state state = state_quote;
214 const WCHAR *p;
215 WCHAR *out = value;
216 BOOL ignore, in_quotes = FALSE;
217 int count = 0, len = 0;
219 for (p = str; *p; p++)
221 ignore = FALSE;
222 switch (state)
224 case state_whitespace:
225 switch (*p)
227 case ' ':
228 in_quotes = TRUE;
229 ignore = TRUE;
230 len++;
231 break;
232 case '"':
233 state = state_quote;
234 if (in_quotes && p[1] != '\"') count--;
235 else count++;
236 break;
237 default:
238 state = state_token;
239 in_quotes = TRUE;
240 len++;
241 break;
243 break;
245 case state_token:
246 switch (*p)
248 case '"':
249 state = state_quote;
250 if (in_quotes) count--;
251 else count++;
252 break;
253 case ' ':
254 state = state_whitespace;
255 if (!count) goto done;
256 in_quotes = TRUE;
257 len++;
258 break;
259 default:
260 if (count) in_quotes = TRUE;
261 len++;
262 break;
264 break;
266 case state_quote:
267 switch (*p)
269 case '"':
270 if (in_quotes && p[1] != '\"') count--;
271 else count++;
272 break;
273 case ' ':
274 state = state_whitespace;
275 if (!count || (count > 1 && !len)) goto done;
276 in_quotes = TRUE;
277 len++;
278 break;
279 default:
280 state = state_token;
281 if (count) in_quotes = TRUE;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
290 if (!count) in_quotes = FALSE;
293 done:
294 if (!len) *value = 0;
295 else *out = 0;
297 *quotes = count;
298 return p - str;
301 static void remove_quotes( WCHAR *str )
303 WCHAR *p = str;
304 int len = strlenW( str );
306 while ((p = strchrW( p, '"' )))
308 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
309 p++;
313 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
314 BOOL preserve_case )
316 LPCWSTR ptr, ptr2;
317 int num_quotes;
318 DWORD len;
319 WCHAR *prop, *val;
320 UINT r;
322 if (!szCommandLine)
323 return ERROR_SUCCESS;
325 ptr = szCommandLine;
326 while (*ptr)
328 while (*ptr == ' ') ptr++;
329 if (!*ptr) break;
331 ptr2 = strchrW( ptr, '=' );
332 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
334 len = ptr2 - ptr;
335 if (!len) return ERROR_INVALID_COMMAND_LINE;
337 while (ptr[len - 1] == ' ') len--;
339 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
340 memcpy( prop, ptr, len * sizeof(WCHAR) );
341 prop[len] = 0;
342 if (!preserve_case) struprW( prop );
344 ptr2++;
345 while (*ptr2 == ' ') ptr2++;
347 num_quotes = 0;
348 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
349 len = parse_prop( ptr2, val, &num_quotes );
350 if (num_quotes % 2)
352 WARN("unbalanced quotes\n");
353 msi_free( val );
354 msi_free( prop );
355 return ERROR_INVALID_COMMAND_LINE;
357 remove_quotes( val );
358 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
360 r = msi_set_property( package->db, prop, val, -1 );
361 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
362 msi_reset_folders( package, TRUE );
364 msi_free( val );
365 msi_free( prop );
367 ptr = ptr2 + len;
370 return ERROR_SUCCESS;
373 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
375 LPCWSTR pc;
376 LPWSTR p, *ret = NULL;
377 UINT count = 0;
379 if (!str)
380 return ret;
382 /* count the number of substrings */
383 for ( pc = str, count = 0; pc; count++ )
385 pc = strchrW( pc, sep );
386 if (pc)
387 pc++;
390 /* allocate space for an array of substring pointers and the substrings */
391 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
392 (lstrlenW(str)+1) * sizeof(WCHAR) );
393 if (!ret)
394 return ret;
396 /* copy the string and set the pointers */
397 p = (LPWSTR) &ret[count+1];
398 lstrcpyW( p, str );
399 for( count = 0; (ret[count] = p); count++ )
401 p = strchrW( p, sep );
402 if (p)
403 *p++ = 0;
406 return ret;
409 static BOOL ui_sequence_exists( MSIPACKAGE *package )
411 static const WCHAR query [] = {
412 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
413 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
414 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
415 MSIQUERY *view;
416 DWORD count = 0;
418 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
420 MSI_IterateRecords( view, &count, NULL, package );
421 msiobj_release( &view->hdr );
423 return count != 0;
426 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
428 WCHAR *source, *check, *p, *db;
429 DWORD len;
431 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
432 return ERROR_OUTOFMEMORY;
434 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
436 msi_free(db);
437 return ERROR_SUCCESS;
439 len = p - db + 2;
440 source = msi_alloc( len * sizeof(WCHAR) );
441 lstrcpynW( source, db, len );
442 msi_free( db );
444 check = msi_dup_property( package->db, szSourceDir );
445 if (!check || replace)
447 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
448 if (r == ERROR_SUCCESS)
449 msi_reset_folders( package, TRUE );
451 msi_free( check );
453 check = msi_dup_property( package->db, szSOURCEDIR );
454 if (!check || replace)
455 msi_set_property( package->db, szSOURCEDIR, source, -1 );
457 msi_free( check );
458 msi_free( source );
460 return ERROR_SUCCESS;
463 static BOOL needs_ui_sequence(MSIPACKAGE *package)
465 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
468 UINT msi_set_context(MSIPACKAGE *package)
470 UINT r = msi_locate_product( package->ProductCode, &package->Context );
471 if (r != ERROR_SUCCESS)
473 int num = msi_get_property_int( package->db, szAllUsers, 0 );
474 if (num == 1 || num == 2)
475 package->Context = MSIINSTALLCONTEXT_MACHINE;
476 else
477 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
479 return ERROR_SUCCESS;
482 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
484 UINT rc;
485 LPCWSTR cond, action;
486 MSIPACKAGE *package = param;
488 action = MSI_RecordGetString(row,1);
489 if (!action)
491 ERR("Error is retrieving action name\n");
492 return ERROR_FUNCTION_FAILED;
495 /* check conditions */
496 cond = MSI_RecordGetString(row,2);
498 /* this is a hack to skip errors in the condition code */
499 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
501 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
502 return ERROR_SUCCESS;
505 if (needs_ui_sequence(package))
506 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
507 else
508 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
510 msi_dialog_check_messages( NULL );
512 if (package->CurrentInstallState != ERROR_SUCCESS)
513 rc = package->CurrentInstallState;
515 if (rc == ERROR_FUNCTION_NOT_CALLED)
516 rc = ERROR_SUCCESS;
518 if (rc != ERROR_SUCCESS)
519 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
521 if (package->need_reboot_now)
523 TRACE("action %s asked for immediate reboot, suspending installation\n",
524 debugstr_w(action));
525 rc = ACTION_ForceReboot( package );
527 return rc;
530 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
532 static const WCHAR query[] = {
533 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
534 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
535 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
536 '`','S','e','q','u','e','n','c','e','`',0};
537 MSIQUERY *view;
538 UINT r;
540 TRACE("%p %s\n", package, debugstr_w(table));
542 r = MSI_OpenQuery( package->db, &view, query, table );
543 if (r == ERROR_SUCCESS)
545 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
546 msiobj_release(&view->hdr);
548 return r;
551 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
553 static const WCHAR query[] = {
554 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
555 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
556 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
557 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
558 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
559 static const WCHAR query_validate[] = {
560 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
561 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
562 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
563 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
564 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
565 MSIQUERY *view;
566 INT seq = 0;
567 UINT rc;
569 if (package->script->ExecuteSequenceRun)
571 TRACE("Execute Sequence already Run\n");
572 return ERROR_SUCCESS;
575 package->script->ExecuteSequenceRun = TRUE;
577 /* get the sequence number */
578 if (UIran)
580 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
581 if (!row) return ERROR_FUNCTION_FAILED;
582 seq = MSI_RecordGetInteger(row,1);
583 msiobj_release(&row->hdr);
585 rc = MSI_OpenQuery(package->db, &view, query, seq);
586 if (rc == ERROR_SUCCESS)
588 TRACE("Running the actions\n");
590 msi_set_property( package->db, szSourceDir, NULL, -1 );
591 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
592 msiobj_release(&view->hdr);
594 return rc;
597 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
599 static const WCHAR query[] = {
600 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
601 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
602 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
603 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
604 MSIQUERY *view;
605 UINT rc;
607 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
608 if (rc == ERROR_SUCCESS)
610 TRACE("Running the actions\n");
611 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
612 msiobj_release(&view->hdr);
614 return rc;
617 /********************************************************
618 * ACTION helper functions and functions that perform the actions
619 *******************************************************/
620 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
622 BOOL ret=FALSE;
623 UINT arc;
625 arc = ACTION_CustomAction( package, action, script );
626 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
628 *rc = arc;
629 ret = TRUE;
631 return ret;
634 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
636 MSICOMPONENT *comp;
638 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
640 if (!strcmpW( Component, comp->Component )) return comp;
642 return NULL;
645 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
647 MSIFEATURE *feature;
649 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
651 if (!strcmpW( Feature, feature->Feature )) return feature;
653 return NULL;
656 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
658 MSIFILE *file;
660 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
662 if (!strcmpW( key, file->File )) return file;
664 return NULL;
667 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
669 MSIFOLDER *folder;
671 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
673 if (!strcmpW( dir, folder->Directory )) return folder;
675 return NULL;
679 * Recursively create all directories in the path.
680 * shamelessly stolen from setupapi/queue.c
682 BOOL msi_create_full_path( const WCHAR *path )
684 BOOL ret = TRUE;
685 WCHAR *new_path;
686 int len;
688 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
689 strcpyW( new_path, path );
691 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
692 new_path[len - 1] = 0;
694 while (!CreateDirectoryW( new_path, NULL ))
696 WCHAR *slash;
697 DWORD last_error = GetLastError();
698 if (last_error == ERROR_ALREADY_EXISTS) break;
699 if (last_error != ERROR_PATH_NOT_FOUND)
701 ret = FALSE;
702 break;
704 if (!(slash = strrchrW( new_path, '\\' )))
706 ret = FALSE;
707 break;
709 len = slash - new_path;
710 new_path[len] = 0;
711 if (!msi_create_full_path( new_path ))
713 ret = FALSE;
714 break;
716 new_path[len] = '\\';
718 msi_free( new_path );
719 return ret;
722 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
724 MSIRECORD *row;
726 row = MSI_CreateRecord( 4 );
727 MSI_RecordSetInteger( row, 1, a );
728 MSI_RecordSetInteger( row, 2, b );
729 MSI_RecordSetInteger( row, 3, c );
730 MSI_RecordSetInteger( row, 4, d );
731 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
732 msiobj_release( &row->hdr );
734 msi_dialog_check_messages( NULL );
737 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
739 static const WCHAR query[] =
740 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
741 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
742 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
743 WCHAR message[1024];
744 MSIRECORD *row = 0;
745 DWORD size;
747 if (!package->LastAction || strcmpW( package->LastAction, action ))
749 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
751 if (MSI_RecordIsNull( row, 3 ))
753 msiobj_release( &row->hdr );
754 return;
756 /* update the cached action format */
757 msi_free( package->ActionFormat );
758 package->ActionFormat = msi_dup_record_field( row, 3 );
759 msi_free( package->LastAction );
760 package->LastAction = strdupW( action );
761 msiobj_release( &row->hdr );
763 size = 1024;
764 MSI_RecordSetStringW( record, 0, package->ActionFormat );
765 MSI_FormatRecordW( package, record, message, &size );
766 row = MSI_CreateRecord( 1 );
767 MSI_RecordSetStringW( row, 1, message );
768 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
769 msiobj_release( &row->hdr );
772 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
774 if (!comp->Enabled)
776 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
777 return INSTALLSTATE_UNKNOWN;
779 if (package->need_rollback) return comp->Installed;
780 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
782 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
783 return INSTALLSTATE_UNKNOWN;
785 return comp->ActionRequest;
788 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
790 if (package->need_rollback) return feature->Installed;
791 return feature->ActionRequest;
794 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
796 MSIPACKAGE *package = param;
797 LPCWSTR dir, component, full_path;
798 MSIRECORD *uirow;
799 MSIFOLDER *folder;
800 MSICOMPONENT *comp;
802 component = MSI_RecordGetString(row, 2);
803 if (!component)
804 return ERROR_SUCCESS;
806 comp = msi_get_loaded_component(package, component);
807 if (!comp)
808 return ERROR_SUCCESS;
810 comp->Action = msi_get_component_action( package, comp );
811 if (comp->Action != INSTALLSTATE_LOCAL)
813 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
814 return ERROR_SUCCESS;
817 dir = MSI_RecordGetString(row,1);
818 if (!dir)
820 ERR("Unable to get folder id\n");
821 return ERROR_SUCCESS;
824 uirow = MSI_CreateRecord(1);
825 MSI_RecordSetStringW(uirow, 1, dir);
826 msi_ui_actiondata(package, szCreateFolders, uirow);
827 msiobj_release(&uirow->hdr);
829 full_path = msi_get_target_folder( package, dir );
830 if (!full_path)
832 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
833 return ERROR_SUCCESS;
835 TRACE("folder is %s\n", debugstr_w(full_path));
837 folder = msi_get_loaded_folder( package, dir );
838 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
839 folder->State = FOLDER_STATE_CREATED;
840 return ERROR_SUCCESS;
843 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
845 static const WCHAR query[] = {
846 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
847 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
848 MSIQUERY *view;
849 UINT rc;
851 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
852 if (rc != ERROR_SUCCESS)
853 return ERROR_SUCCESS;
855 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
856 msiobj_release(&view->hdr);
857 return rc;
860 static void remove_persistent_folder( MSIFOLDER *folder )
862 FolderList *fl;
864 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
866 remove_persistent_folder( fl->folder );
868 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
870 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
874 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
876 MSIPACKAGE *package = param;
877 LPCWSTR dir, component, full_path;
878 MSIRECORD *uirow;
879 MSIFOLDER *folder;
880 MSICOMPONENT *comp;
882 component = MSI_RecordGetString(row, 2);
883 if (!component)
884 return ERROR_SUCCESS;
886 comp = msi_get_loaded_component(package, component);
887 if (!comp)
888 return ERROR_SUCCESS;
890 comp->Action = msi_get_component_action( package, comp );
891 if (comp->Action != INSTALLSTATE_ABSENT)
893 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
894 return ERROR_SUCCESS;
897 dir = MSI_RecordGetString( row, 1 );
898 if (!dir)
900 ERR("Unable to get folder id\n");
901 return ERROR_SUCCESS;
904 full_path = msi_get_target_folder( package, dir );
905 if (!full_path)
907 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
908 return ERROR_SUCCESS;
910 TRACE("folder is %s\n", debugstr_w(full_path));
912 uirow = MSI_CreateRecord( 1 );
913 MSI_RecordSetStringW( uirow, 1, dir );
914 msi_ui_actiondata( package, szRemoveFolders, uirow );
915 msiobj_release( &uirow->hdr );
917 folder = msi_get_loaded_folder( package, dir );
918 remove_persistent_folder( folder );
919 return ERROR_SUCCESS;
922 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
924 static const WCHAR query[] = {
925 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
926 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
927 MSIQUERY *view;
928 UINT rc;
930 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
931 if (rc != ERROR_SUCCESS)
932 return ERROR_SUCCESS;
934 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
935 msiobj_release( &view->hdr );
936 return rc;
939 static UINT load_component( MSIRECORD *row, LPVOID param )
941 MSIPACKAGE *package = param;
942 MSICOMPONENT *comp;
944 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
945 if (!comp)
946 return ERROR_FUNCTION_FAILED;
948 list_add_tail( &package->components, &comp->entry );
950 /* fill in the data */
951 comp->Component = msi_dup_record_field( row, 1 );
953 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
955 comp->ComponentId = msi_dup_record_field( row, 2 );
956 comp->Directory = msi_dup_record_field( row, 3 );
957 comp->Attributes = MSI_RecordGetInteger(row,4);
958 comp->Condition = msi_dup_record_field( row, 5 );
959 comp->KeyPath = msi_dup_record_field( row, 6 );
961 comp->Installed = INSTALLSTATE_UNKNOWN;
962 comp->Action = INSTALLSTATE_UNKNOWN;
963 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
965 comp->assembly = msi_load_assembly( package, comp );
966 return ERROR_SUCCESS;
969 UINT msi_load_all_components( MSIPACKAGE *package )
971 static const WCHAR query[] = {
972 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
973 '`','C','o','m','p','o','n','e','n','t','`',0};
974 MSIQUERY *view;
975 UINT r;
977 if (!list_empty(&package->components))
978 return ERROR_SUCCESS;
980 r = MSI_DatabaseOpenViewW( package->db, query, &view );
981 if (r != ERROR_SUCCESS)
982 return r;
984 if (!msi_init_assembly_caches( package ))
986 ERR("can't initialize assembly caches\n");
987 msiobj_release( &view->hdr );
988 return ERROR_FUNCTION_FAILED;
991 r = MSI_IterateRecords(view, NULL, load_component, package);
992 msiobj_release(&view->hdr);
993 return r;
996 typedef struct {
997 MSIPACKAGE *package;
998 MSIFEATURE *feature;
999 } _ilfs;
1001 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1003 ComponentList *cl;
1005 cl = msi_alloc( sizeof (*cl) );
1006 if ( !cl )
1007 return ERROR_NOT_ENOUGH_MEMORY;
1008 cl->component = comp;
1009 list_add_tail( &feature->Components, &cl->entry );
1011 return ERROR_SUCCESS;
1014 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1016 FeatureList *fl;
1018 fl = msi_alloc( sizeof(*fl) );
1019 if ( !fl )
1020 return ERROR_NOT_ENOUGH_MEMORY;
1021 fl->feature = child;
1022 list_add_tail( &parent->Children, &fl->entry );
1024 return ERROR_SUCCESS;
1027 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1029 _ilfs* ilfs = param;
1030 LPCWSTR component;
1031 MSICOMPONENT *comp;
1033 component = MSI_RecordGetString(row,1);
1035 /* check to see if the component is already loaded */
1036 comp = msi_get_loaded_component( ilfs->package, component );
1037 if (!comp)
1039 WARN("ignoring unknown component %s\n", debugstr_w(component));
1040 return ERROR_SUCCESS;
1042 add_feature_component( ilfs->feature, comp );
1043 comp->Enabled = TRUE;
1045 return ERROR_SUCCESS;
1048 static UINT load_feature(MSIRECORD * row, LPVOID param)
1050 static const WCHAR query[] = {
1051 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1052 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1053 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1054 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1055 MSIPACKAGE *package = param;
1056 MSIFEATURE *feature;
1057 MSIQUERY *view;
1058 _ilfs ilfs;
1059 UINT rc;
1061 /* fill in the data */
1063 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1064 if (!feature)
1065 return ERROR_NOT_ENOUGH_MEMORY;
1067 list_init( &feature->Children );
1068 list_init( &feature->Components );
1070 feature->Feature = msi_dup_record_field( row, 1 );
1072 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1074 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1075 feature->Title = msi_dup_record_field( row, 3 );
1076 feature->Description = msi_dup_record_field( row, 4 );
1078 if (!MSI_RecordIsNull(row,5))
1079 feature->Display = MSI_RecordGetInteger(row,5);
1081 feature->Level= MSI_RecordGetInteger(row,6);
1082 feature->Directory = msi_dup_record_field( row, 7 );
1083 feature->Attributes = MSI_RecordGetInteger(row,8);
1085 feature->Installed = INSTALLSTATE_UNKNOWN;
1086 feature->Action = INSTALLSTATE_UNKNOWN;
1087 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1089 list_add_tail( &package->features, &feature->entry );
1091 /* load feature components */
1093 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1094 if (rc != ERROR_SUCCESS)
1095 return ERROR_SUCCESS;
1097 ilfs.package = package;
1098 ilfs.feature = feature;
1100 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1101 msiobj_release(&view->hdr);
1102 return rc;
1105 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1107 MSIPACKAGE *package = param;
1108 MSIFEATURE *parent, *child;
1110 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1111 if (!child)
1112 return ERROR_FUNCTION_FAILED;
1114 if (!child->Feature_Parent)
1115 return ERROR_SUCCESS;
1117 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1118 if (!parent)
1119 return ERROR_FUNCTION_FAILED;
1121 add_feature_child( parent, child );
1122 return ERROR_SUCCESS;
1125 UINT msi_load_all_features( MSIPACKAGE *package )
1127 static const WCHAR query[] = {
1128 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1129 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1130 '`','D','i','s','p','l','a','y','`',0};
1131 MSIQUERY *view;
1132 UINT r;
1134 if (!list_empty(&package->features))
1135 return ERROR_SUCCESS;
1137 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1138 if (r != ERROR_SUCCESS)
1139 return r;
1141 r = MSI_IterateRecords( view, NULL, load_feature, package );
1142 if (r != ERROR_SUCCESS)
1144 msiobj_release( &view->hdr );
1145 return r;
1147 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1148 msiobj_release( &view->hdr );
1149 return r;
1152 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1154 if (!p)
1155 return p;
1156 p = strchrW(p, ch);
1157 if (!p)
1158 return p;
1159 *p = 0;
1160 return p+1;
1163 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1165 static const WCHAR query[] = {
1166 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1167 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1168 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1169 MSIQUERY *view = NULL;
1170 MSIRECORD *row = NULL;
1171 UINT r;
1173 TRACE("%s\n", debugstr_w(file->File));
1175 r = MSI_OpenQuery(package->db, &view, query, file->File);
1176 if (r != ERROR_SUCCESS)
1177 goto done;
1179 r = MSI_ViewExecute(view, NULL);
1180 if (r != ERROR_SUCCESS)
1181 goto done;
1183 r = MSI_ViewFetch(view, &row);
1184 if (r != ERROR_SUCCESS)
1185 goto done;
1187 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1188 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1189 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1190 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1191 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1193 done:
1194 if (view) msiobj_release(&view->hdr);
1195 if (row) msiobj_release(&row->hdr);
1196 return r;
1199 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1201 MSIRECORD *row;
1202 static const WCHAR query[] = {
1203 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1204 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1205 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1207 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1208 if (!row)
1210 WARN("query failed\n");
1211 return ERROR_FUNCTION_FAILED;
1214 file->disk_id = MSI_RecordGetInteger( row, 1 );
1215 msiobj_release( &row->hdr );
1216 return ERROR_SUCCESS;
1219 static UINT load_file(MSIRECORD *row, LPVOID param)
1221 MSIPACKAGE* package = param;
1222 LPCWSTR component;
1223 MSIFILE *file;
1225 /* fill in the data */
1227 file = msi_alloc_zero( sizeof (MSIFILE) );
1228 if (!file)
1229 return ERROR_NOT_ENOUGH_MEMORY;
1231 file->File = msi_dup_record_field( row, 1 );
1233 component = MSI_RecordGetString( row, 2 );
1234 file->Component = msi_get_loaded_component( package, component );
1236 if (!file->Component)
1238 WARN("Component not found: %s\n", debugstr_w(component));
1239 msi_free(file->File);
1240 msi_free(file);
1241 return ERROR_SUCCESS;
1244 file->FileName = msi_dup_record_field( row, 3 );
1245 msi_reduce_to_long_filename( file->FileName );
1247 file->ShortName = msi_dup_record_field( row, 3 );
1248 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1250 file->FileSize = MSI_RecordGetInteger( row, 4 );
1251 file->Version = msi_dup_record_field( row, 5 );
1252 file->Language = msi_dup_record_field( row, 6 );
1253 file->Attributes = MSI_RecordGetInteger( row, 7 );
1254 file->Sequence = MSI_RecordGetInteger( row, 8 );
1256 file->state = msifs_invalid;
1258 /* if the compressed bits are not set in the file attributes,
1259 * then read the information from the package word count property
1261 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1263 file->IsCompressed = FALSE;
1265 else if (file->Attributes &
1266 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1268 file->IsCompressed = TRUE;
1270 else if (file->Attributes & msidbFileAttributesNoncompressed)
1272 file->IsCompressed = FALSE;
1274 else
1276 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1279 load_file_hash(package, file);
1280 load_file_disk_id(package, file);
1282 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1284 list_add_tail( &package->files, &file->entry );
1286 return ERROR_SUCCESS;
1289 static UINT load_all_files(MSIPACKAGE *package)
1291 static const WCHAR query[] = {
1292 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1293 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1294 '`','S','e','q','u','e','n','c','e','`', 0};
1295 MSIQUERY *view;
1296 UINT rc;
1298 if (!list_empty(&package->files))
1299 return ERROR_SUCCESS;
1301 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1302 if (rc != ERROR_SUCCESS)
1303 return ERROR_SUCCESS;
1305 rc = MSI_IterateRecords(view, NULL, load_file, package);
1306 msiobj_release(&view->hdr);
1307 return rc;
1310 static UINT load_media( MSIRECORD *row, LPVOID param )
1312 MSIPACKAGE *package = param;
1313 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1314 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1316 /* FIXME: load external cabinets and directory sources too */
1317 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1318 return ERROR_SUCCESS;
1320 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1323 static UINT load_all_media( MSIPACKAGE *package )
1325 static const WCHAR query[] = {
1326 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1327 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1328 '`','D','i','s','k','I','d','`',0};
1329 MSIQUERY *view;
1330 UINT r;
1332 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1333 if (r != ERROR_SUCCESS)
1334 return ERROR_SUCCESS;
1336 r = MSI_IterateRecords( view, NULL, load_media, package );
1337 msiobj_release( &view->hdr );
1338 return r;
1341 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1343 static const WCHAR query[] =
1344 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1345 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1346 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1347 MSIRECORD *rec;
1349 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1351 WARN("query failed\n");
1352 return ERROR_FUNCTION_FAILED;
1355 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1356 msiobj_release( &rec->hdr );
1357 return ERROR_SUCCESS;
1360 static UINT load_patch(MSIRECORD *row, LPVOID param)
1362 MSIPACKAGE *package = param;
1363 MSIFILEPATCH *patch;
1364 const WCHAR *file_key;
1366 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1367 if (!patch)
1368 return ERROR_NOT_ENOUGH_MEMORY;
1370 file_key = MSI_RecordGetString( row, 1 );
1371 patch->File = msi_get_loaded_file( package, file_key );
1372 if (!patch->File)
1374 ERR("Failed to find target for patch in File table\n");
1375 msi_free(patch);
1376 return ERROR_FUNCTION_FAILED;
1379 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1380 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1381 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1383 /* FIXME:
1384 * Header field - for patch validation.
1385 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1388 load_patch_disk_id( package, patch );
1390 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1392 list_add_tail( &package->filepatches, &patch->entry );
1394 return ERROR_SUCCESS;
1397 static UINT load_all_patches(MSIPACKAGE *package)
1399 static const WCHAR query[] = {
1400 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1401 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1402 '`','S','e','q','u','e','n','c','e','`',0};
1403 MSIQUERY *view;
1404 UINT rc;
1406 if (!list_empty(&package->filepatches))
1407 return ERROR_SUCCESS;
1409 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1410 if (rc != ERROR_SUCCESS)
1411 return ERROR_SUCCESS;
1413 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1414 msiobj_release(&view->hdr);
1415 return rc;
1418 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1420 static const WCHAR query[] = {
1421 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1422 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1423 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1424 MSIQUERY *view;
1426 folder->persistent = FALSE;
1427 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1429 if (!MSI_ViewExecute( view, NULL ))
1431 MSIRECORD *rec;
1432 if (!MSI_ViewFetch( view, &rec ))
1434 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1435 folder->persistent = TRUE;
1436 msiobj_release( &rec->hdr );
1439 msiobj_release( &view->hdr );
1441 return ERROR_SUCCESS;
1444 static UINT load_folder( MSIRECORD *row, LPVOID param )
1446 MSIPACKAGE *package = param;
1447 static WCHAR szEmpty[] = { 0 };
1448 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1449 MSIFOLDER *folder;
1451 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1452 list_init( &folder->children );
1453 folder->Directory = msi_dup_record_field( row, 1 );
1454 folder->Parent = msi_dup_record_field( row, 2 );
1455 p = msi_dup_record_field(row, 3);
1457 TRACE("%s\n", debugstr_w(folder->Directory));
1459 /* split src and target dir */
1460 tgt_short = p;
1461 src_short = folder_split_path( p, ':' );
1463 /* split the long and short paths */
1464 tgt_long = folder_split_path( tgt_short, '|' );
1465 src_long = folder_split_path( src_short, '|' );
1467 /* check for no-op dirs */
1468 if (tgt_short && !strcmpW( szDot, tgt_short ))
1469 tgt_short = szEmpty;
1470 if (src_short && !strcmpW( szDot, src_short ))
1471 src_short = szEmpty;
1473 if (!tgt_long)
1474 tgt_long = tgt_short;
1476 if (!src_short) {
1477 src_short = tgt_short;
1478 src_long = tgt_long;
1481 if (!src_long)
1482 src_long = src_short;
1484 /* FIXME: use the target short path too */
1485 folder->TargetDefault = strdupW(tgt_long);
1486 folder->SourceShortPath = strdupW(src_short);
1487 folder->SourceLongPath = strdupW(src_long);
1488 msi_free(p);
1490 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1491 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1492 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1494 load_folder_persistence( package, folder );
1496 list_add_tail( &package->folders, &folder->entry );
1497 return ERROR_SUCCESS;
1500 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1502 FolderList *fl;
1504 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1505 fl->folder = child;
1506 list_add_tail( &parent->children, &fl->entry );
1507 return ERROR_SUCCESS;
1510 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1512 MSIPACKAGE *package = param;
1513 MSIFOLDER *parent, *child;
1515 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1516 return ERROR_FUNCTION_FAILED;
1518 if (!child->Parent) return ERROR_SUCCESS;
1520 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1521 return ERROR_FUNCTION_FAILED;
1523 return add_folder_child( parent, child );
1526 static UINT load_all_folders( MSIPACKAGE *package )
1528 static const WCHAR query[] = {
1529 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1530 '`','D','i','r','e','c','t','o','r','y','`',0};
1531 MSIQUERY *view;
1532 UINT r;
1534 if (!list_empty(&package->folders))
1535 return ERROR_SUCCESS;
1537 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1538 if (r != ERROR_SUCCESS)
1539 return r;
1541 r = MSI_IterateRecords( view, NULL, load_folder, package );
1542 if (r != ERROR_SUCCESS)
1544 msiobj_release( &view->hdr );
1545 return r;
1547 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1548 msiobj_release( &view->hdr );
1549 return r;
1552 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1554 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1555 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1557 load_all_folders( package );
1558 msi_load_all_components( package );
1559 msi_load_all_features( package );
1560 load_all_files( package );
1561 load_all_patches( package );
1562 load_all_media( package );
1564 return ERROR_SUCCESS;
1567 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1569 const WCHAR *action = package->script->Actions[script][index];
1570 ui_actionstart( package, action );
1571 TRACE("executing %s\n", debugstr_w(action));
1572 return ACTION_PerformAction( package, action, script );
1575 static UINT execute_script( MSIPACKAGE *package, UINT script )
1577 UINT i, rc = ERROR_SUCCESS;
1579 TRACE("executing script %u\n", script);
1581 if (!package->script)
1583 ERR("no script!\n");
1584 return ERROR_FUNCTION_FAILED;
1586 if (script == SCRIPT_ROLLBACK)
1588 for (i = package->script->ActionCount[script]; i > 0; i--)
1590 rc = execute_script_action( package, script, i - 1 );
1591 if (rc != ERROR_SUCCESS) break;
1594 else
1596 for (i = 0; i < package->script->ActionCount[script]; i++)
1598 rc = execute_script_action( package, script, i );
1599 if (rc != ERROR_SUCCESS) break;
1602 msi_free_action_script(package, script);
1603 return rc;
1606 static UINT ACTION_FileCost(MSIPACKAGE *package)
1608 return ERROR_SUCCESS;
1611 static void get_client_counts( MSIPACKAGE *package )
1613 MSICOMPONENT *comp;
1614 HKEY hkey;
1616 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1618 if (!comp->ComponentId) continue;
1620 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1621 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1623 comp->num_clients = 0;
1624 continue;
1626 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1627 NULL, NULL, NULL, NULL );
1628 RegCloseKey( hkey );
1632 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1634 MSICOMPONENT *comp;
1635 UINT r;
1637 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1639 if (!comp->ComponentId) continue;
1641 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1642 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1643 &comp->Installed );
1644 if (r == ERROR_SUCCESS) continue;
1646 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1647 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1648 &comp->Installed );
1649 if (r == ERROR_SUCCESS) continue;
1651 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1652 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1653 &comp->Installed );
1654 if (r == ERROR_SUCCESS) continue;
1656 comp->Installed = INSTALLSTATE_ABSENT;
1660 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1662 MSIFEATURE *feature;
1664 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1666 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1668 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1669 feature->Installed = INSTALLSTATE_ABSENT;
1670 else
1671 feature->Installed = state;
1675 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1677 return (feature->Level > 0 && feature->Level <= level);
1680 static BOOL process_state_property(MSIPACKAGE* package, int level,
1681 LPCWSTR property, INSTALLSTATE state)
1683 LPWSTR override;
1684 MSIFEATURE *feature;
1685 BOOL remove = !strcmpW(property, szRemove);
1686 BOOL reinstall = !strcmpW(property, szReinstall);
1688 override = msi_dup_property( package->db, property );
1689 if (!override)
1690 return FALSE;
1692 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1694 if (feature->Level <= 0)
1695 continue;
1697 if (reinstall)
1698 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1699 else if (remove)
1700 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1702 if (!strcmpiW( override, szAll ))
1704 feature->Action = state;
1705 feature->ActionRequest = state;
1707 else
1709 LPWSTR ptr = override;
1710 LPWSTR ptr2 = strchrW(override,',');
1712 while (ptr)
1714 int len = ptr2 - ptr;
1716 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1717 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1719 feature->Action = state;
1720 feature->ActionRequest = state;
1721 break;
1723 if (ptr2)
1725 ptr=ptr2+1;
1726 ptr2 = strchrW(ptr,',');
1728 else
1729 break;
1733 msi_free(override);
1734 return TRUE;
1737 static BOOL process_overrides( MSIPACKAGE *package, int level )
1739 static const WCHAR szAddLocal[] =
1740 {'A','D','D','L','O','C','A','L',0};
1741 static const WCHAR szAddSource[] =
1742 {'A','D','D','S','O','U','R','C','E',0};
1743 static const WCHAR szAdvertise[] =
1744 {'A','D','V','E','R','T','I','S','E',0};
1745 BOOL ret = FALSE;
1747 /* all these activation/deactivation things happen in order and things
1748 * later on the list override things earlier on the list.
1750 * 0 INSTALLLEVEL processing
1751 * 1 ADDLOCAL
1752 * 2 REMOVE
1753 * 3 ADDSOURCE
1754 * 4 ADDDEFAULT
1755 * 5 REINSTALL
1756 * 6 ADVERTISE
1757 * 7 COMPADDLOCAL
1758 * 8 COMPADDSOURCE
1759 * 9 FILEADDLOCAL
1760 * 10 FILEADDSOURCE
1761 * 11 FILEADDDEFAULT
1763 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1764 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1765 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1766 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1767 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1769 if (ret && !package->full_reinstall)
1770 msi_set_property( package->db, szPreselected, szOne, -1 );
1772 return ret;
1775 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1777 int level;
1778 MSICOMPONENT* component;
1779 MSIFEATURE *feature;
1781 TRACE("Checking Install Level\n");
1783 level = msi_get_property_int(package->db, szInstallLevel, 1);
1785 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1787 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1789 if (!is_feature_selected( feature, level )) continue;
1791 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1793 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1795 feature->Action = INSTALLSTATE_SOURCE;
1796 feature->ActionRequest = INSTALLSTATE_SOURCE;
1798 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1800 feature->Action = INSTALLSTATE_ADVERTISED;
1801 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1803 else
1805 feature->Action = INSTALLSTATE_LOCAL;
1806 feature->ActionRequest = INSTALLSTATE_LOCAL;
1810 /* disable child features of unselected parent or follow parent */
1811 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1813 FeatureList *fl;
1815 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1817 if (!is_feature_selected( feature, level ))
1819 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1820 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1822 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1824 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1825 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1826 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1827 fl->feature->Action = feature->Action;
1828 fl->feature->ActionRequest = feature->ActionRequest;
1833 else /* preselected */
1835 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1837 if (!is_feature_selected( feature, level )) continue;
1839 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1841 if (feature->Installed == INSTALLSTATE_ABSENT)
1843 feature->Action = INSTALLSTATE_UNKNOWN;
1844 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1846 else
1848 feature->Action = feature->Installed;
1849 feature->ActionRequest = feature->Installed;
1853 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1855 FeatureList *fl;
1857 if (!is_feature_selected( feature, level )) continue;
1859 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1861 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1862 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1864 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1865 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1866 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1867 fl->feature->Action = feature->Action;
1868 fl->feature->ActionRequest = feature->ActionRequest;
1874 /* now we want to set component state based based on feature state */
1875 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1877 ComponentList *cl;
1879 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1880 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1881 feature->ActionRequest, feature->Action);
1883 /* features with components that have compressed files are made local */
1884 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1886 if (cl->component->ForceLocalState &&
1887 feature->ActionRequest == INSTALLSTATE_SOURCE)
1889 feature->Action = INSTALLSTATE_LOCAL;
1890 feature->ActionRequest = INSTALLSTATE_LOCAL;
1891 break;
1895 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1897 component = cl->component;
1899 switch (feature->ActionRequest)
1901 case INSTALLSTATE_ABSENT:
1902 component->anyAbsent = 1;
1903 break;
1904 case INSTALLSTATE_ADVERTISED:
1905 component->hasAdvertisedFeature = 1;
1906 break;
1907 case INSTALLSTATE_SOURCE:
1908 component->hasSourceFeature = 1;
1909 break;
1910 case INSTALLSTATE_LOCAL:
1911 component->hasLocalFeature = 1;
1912 break;
1913 case INSTALLSTATE_DEFAULT:
1914 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1915 component->hasAdvertisedFeature = 1;
1916 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1917 component->hasSourceFeature = 1;
1918 else
1919 component->hasLocalFeature = 1;
1920 break;
1921 default:
1922 break;
1927 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1929 /* check if it's local or source */
1930 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1931 (component->hasLocalFeature || component->hasSourceFeature))
1933 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1934 !component->ForceLocalState)
1936 component->Action = INSTALLSTATE_SOURCE;
1937 component->ActionRequest = INSTALLSTATE_SOURCE;
1939 else
1941 component->Action = INSTALLSTATE_LOCAL;
1942 component->ActionRequest = INSTALLSTATE_LOCAL;
1944 continue;
1947 /* if any feature is local, the component must be local too */
1948 if (component->hasLocalFeature)
1950 component->Action = INSTALLSTATE_LOCAL;
1951 component->ActionRequest = INSTALLSTATE_LOCAL;
1952 continue;
1954 if (component->hasSourceFeature)
1956 component->Action = INSTALLSTATE_SOURCE;
1957 component->ActionRequest = INSTALLSTATE_SOURCE;
1958 continue;
1960 if (component->hasAdvertisedFeature)
1962 component->Action = INSTALLSTATE_ADVERTISED;
1963 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1964 continue;
1966 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1967 if (component->anyAbsent && component->ComponentId)
1969 component->Action = INSTALLSTATE_ABSENT;
1970 component->ActionRequest = INSTALLSTATE_ABSENT;
1974 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1976 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1978 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1979 component->Action = INSTALLSTATE_LOCAL;
1980 component->ActionRequest = INSTALLSTATE_LOCAL;
1983 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1984 component->Installed == INSTALLSTATE_SOURCE &&
1985 component->hasSourceFeature)
1987 component->Action = INSTALLSTATE_UNKNOWN;
1988 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1991 TRACE("component %s (installed %d request %d action %d)\n",
1992 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1994 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1995 component->num_clients++;
1996 else if (component->Action == INSTALLSTATE_ABSENT)
1997 component->num_clients--;
2000 return ERROR_SUCCESS;
2003 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2005 MSIPACKAGE *package = param;
2006 LPCWSTR name;
2007 MSIFEATURE *feature;
2009 name = MSI_RecordGetString( row, 1 );
2011 feature = msi_get_loaded_feature( package, name );
2012 if (!feature)
2013 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2014 else
2016 LPCWSTR Condition;
2017 Condition = MSI_RecordGetString(row,3);
2019 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2021 int level = MSI_RecordGetInteger(row,2);
2022 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2023 feature->Level = level;
2026 return ERROR_SUCCESS;
2029 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2031 static const WCHAR name[] = {'\\',0};
2032 VS_FIXEDFILEINFO *ptr, *ret;
2033 LPVOID version;
2034 DWORD versize, handle;
2035 UINT sz;
2037 versize = GetFileVersionInfoSizeW( filename, &handle );
2038 if (!versize)
2039 return NULL;
2041 version = msi_alloc( versize );
2042 if (!version)
2043 return NULL;
2045 GetFileVersionInfoW( filename, 0, versize, version );
2047 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2049 msi_free( version );
2050 return NULL;
2053 ret = msi_alloc( sz );
2054 memcpy( ret, ptr, sz );
2056 msi_free( version );
2057 return ret;
2060 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2062 DWORD ms, ls;
2064 msi_parse_version_string( version, &ms, &ls );
2066 if (fi->dwFileVersionMS > ms) return 1;
2067 else if (fi->dwFileVersionMS < ms) return -1;
2068 else if (fi->dwFileVersionLS > ls) return 1;
2069 else if (fi->dwFileVersionLS < ls) return -1;
2070 return 0;
2073 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2075 DWORD ms1, ms2;
2077 msi_parse_version_string( ver1, &ms1, NULL );
2078 msi_parse_version_string( ver2, &ms2, NULL );
2080 if (ms1 > ms2) return 1;
2081 else if (ms1 < ms2) return -1;
2082 return 0;
2085 DWORD msi_get_disk_file_size( LPCWSTR filename )
2087 HANDLE file;
2088 DWORD size;
2090 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2091 if (file == INVALID_HANDLE_VALUE)
2092 return INVALID_FILE_SIZE;
2094 size = GetFileSize( file, NULL );
2095 CloseHandle( file );
2096 return size;
2099 BOOL msi_file_hash_matches( MSIFILE *file )
2101 UINT r;
2102 MSIFILEHASHINFO hash;
2104 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2105 r = msi_get_filehash( file->TargetPath, &hash );
2106 if (r != ERROR_SUCCESS)
2107 return FALSE;
2109 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2112 static WCHAR *create_temp_dir( MSIDATABASE *db )
2114 static UINT id;
2115 WCHAR *ret;
2117 if (!db->tempfolder)
2119 WCHAR tmp[MAX_PATH];
2120 UINT len = sizeof(tmp)/sizeof(tmp[0]);
2122 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2123 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2125 GetTempPathW( MAX_PATH, tmp );
2127 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2130 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2132 for (;;)
2134 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2136 msi_free( ret );
2137 return NULL;
2139 if (CreateDirectoryW( ret, NULL )) break;
2143 return ret;
2147 * msi_build_directory_name()
2149 * This function is to save messing round with directory names
2150 * It handles adding backslashes between path segments,
2151 * and can add \ at the end of the directory name if told to.
2153 * It takes a variable number of arguments.
2154 * It always allocates a new string for the result, so make sure
2155 * to free the return value when finished with it.
2157 * The first arg is the number of path segments that follow.
2158 * The arguments following count are a list of path segments.
2159 * A path segment may be NULL.
2161 * Path segments will be added with a \ separating them.
2162 * A \ will not be added after the last segment, however if the
2163 * last segment is NULL, then the last character will be a \
2165 WCHAR *msi_build_directory_name( DWORD count, ... )
2167 DWORD sz = 1, i;
2168 WCHAR *dir;
2169 va_list va;
2171 va_start( va, count );
2172 for (i = 0; i < count; i++)
2174 const WCHAR *str = va_arg( va, const WCHAR * );
2175 if (str) sz += strlenW( str ) + 1;
2177 va_end( va );
2179 dir = msi_alloc( sz * sizeof(WCHAR) );
2180 dir[0] = 0;
2182 va_start( va, count );
2183 for (i = 0; i < count; i++)
2185 const WCHAR *str = va_arg( va, const WCHAR * );
2186 if (!str) continue;
2187 strcatW( dir, str );
2188 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2190 va_end( va );
2191 return dir;
2194 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2196 return comp->assembly && !comp->assembly->application;
2199 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2201 msi_free( file->TargetPath );
2202 if (msi_is_global_assembly( file->Component ))
2204 MSIASSEMBLY *assembly = file->Component->assembly;
2206 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2207 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2209 else
2211 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2212 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2215 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2218 static UINT calculate_file_cost( MSIPACKAGE *package )
2220 VS_FIXEDFILEINFO *file_version;
2221 WCHAR *font_version;
2222 MSIFILE *file;
2224 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2226 MSICOMPONENT *comp = file->Component;
2227 DWORD file_size;
2229 if (!comp->Enabled) continue;
2231 if (file->IsCompressed)
2232 comp->ForceLocalState = TRUE;
2234 set_target_path( package, file );
2236 if ((comp->assembly && !comp->assembly->installed) ||
2237 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2239 comp->Cost += file->FileSize;
2240 continue;
2242 file_size = msi_get_disk_file_size( file->TargetPath );
2243 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2245 if (file->Version)
2247 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2249 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2251 comp->Cost += file->FileSize - file_size;
2253 msi_free( file_version );
2254 continue;
2256 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2258 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2260 comp->Cost += file->FileSize - file_size;
2262 msi_free( font_version );
2263 continue;
2266 if (file_size != file->FileSize)
2268 comp->Cost += file->FileSize - file_size;
2271 return ERROR_SUCCESS;
2274 WCHAR *msi_normalize_path( const WCHAR *in )
2276 const WCHAR *p = in;
2277 WCHAR *q, *ret;
2278 int n, len = strlenW( in ) + 2;
2280 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2282 len = 0;
2283 while (1)
2285 /* copy until the end of the string or a space */
2286 while (*p != ' ' && (*q = *p))
2288 p++, len++;
2289 /* reduce many backslashes to one */
2290 if (*p != '\\' || *q != '\\')
2291 q++;
2294 /* quit at the end of the string */
2295 if (!*p)
2296 break;
2298 /* count the number of spaces */
2299 n = 0;
2300 while (p[n] == ' ')
2301 n++;
2303 /* if it's leading or trailing space, skip it */
2304 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2305 p += n;
2306 else /* copy n spaces */
2307 while (n && (*q++ = *p++)) n--;
2309 while (q - ret > 0 && q[-1] == ' ') q--;
2310 if (q - ret > 0 && q[-1] != '\\')
2312 q[0] = '\\';
2313 q[1] = 0;
2315 return ret;
2318 static WCHAR *get_install_location( MSIPACKAGE *package )
2320 HKEY hkey;
2321 WCHAR *path;
2323 if (!package->ProductCode) return NULL;
2324 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2325 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2327 msi_free( path );
2328 path = NULL;
2330 RegCloseKey( hkey );
2331 return path;
2334 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2336 FolderList *fl;
2337 MSIFOLDER *folder, *parent, *child;
2338 WCHAR *path, *normalized_path;
2340 TRACE("resolving %s\n", debugstr_w(name));
2342 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2344 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2346 if (!(path = get_install_location( package )) &&
2347 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2349 path = msi_dup_property( package->db, szRootDrive );
2352 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2354 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2356 parent = msi_get_loaded_folder( package, folder->Parent );
2357 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2359 else
2360 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2362 normalized_path = msi_normalize_path( path );
2363 msi_free( path );
2364 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2366 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2367 msi_free( normalized_path );
2368 return;
2370 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2371 msi_free( folder->ResolvedTarget );
2372 folder->ResolvedTarget = normalized_path;
2374 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2376 child = fl->folder;
2377 msi_resolve_target_folder( package, child->Directory, load_prop );
2379 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2382 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2384 MSICOMPONENT *comp;
2385 ULONGLONG ret = 0;
2387 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2389 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2391 return ret;
2394 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2396 static const WCHAR query[] =
2397 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2398 '`','C','o','n','d','i','t','i','o','n','`',0};
2399 static const WCHAR szOutOfDiskSpace[] =
2400 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2401 static const WCHAR szPrimaryFolder[] =
2402 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2403 static const WCHAR szPrimaryVolumePath[] =
2404 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2405 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2406 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2407 'A','v','a','i','l','a','b','l','e',0};
2408 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2409 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2410 'R','e','q','u','i','r','e','d',0};
2411 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2412 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2413 'R','e','m','a','i','n','i','n','g',0};
2414 static const WCHAR szOutOfNoRbDiskSpace[] =
2415 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2416 MSICOMPONENT *comp;
2417 MSIQUERY *view;
2418 WCHAR *level, *primary_key, *primary_folder;
2419 UINT rc;
2421 TRACE("Building directory properties\n");
2422 msi_resolve_target_folder( package, szTargetDir, TRUE );
2424 TRACE("Evaluating component conditions\n");
2425 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2427 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2429 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2430 comp->Enabled = FALSE;
2432 else
2433 comp->Enabled = TRUE;
2435 get_client_counts( package );
2437 /* read components states from the registry */
2438 ACTION_GetComponentInstallStates(package);
2439 ACTION_GetFeatureInstallStates(package);
2441 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2443 TRACE("Evaluating feature conditions\n");
2445 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2446 if (rc == ERROR_SUCCESS)
2448 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2449 msiobj_release( &view->hdr );
2450 if (rc != ERROR_SUCCESS)
2451 return rc;
2455 TRACE("Calculating file cost\n");
2456 calculate_file_cost( package );
2458 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2459 /* set default run level if not set */
2460 level = msi_dup_property( package->db, szInstallLevel );
2461 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2462 msi_free(level);
2464 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2466 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2468 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2470 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2471 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2473 static const WCHAR fmtW[] = {'%','l','u',0};
2474 ULARGE_INTEGER free;
2475 ULONGLONG required;
2476 WCHAR buf[21];
2478 primary_folder[2] = 0;
2479 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2481 sprintfW( buf, fmtW, free.QuadPart / 512 );
2482 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2484 required = get_volume_space_required( package );
2485 sprintfW( buf, fmtW, required / 512 );
2486 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2488 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2489 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2490 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2492 msi_free( primary_folder );
2494 msi_free( primary_key );
2497 /* FIXME: check volume disk space */
2498 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2499 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2501 return ERROR_SUCCESS;
2504 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2506 BYTE *data;
2508 if (!value)
2510 *size = sizeof(WCHAR);
2511 *type = REG_SZ;
2512 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2513 return data;
2515 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2517 if (value[1]=='x')
2519 LPWSTR ptr;
2520 CHAR byte[5];
2521 LPWSTR deformated = NULL;
2522 int count;
2524 deformat_string(package, &value[2], &deformated);
2526 /* binary value type */
2527 ptr = deformated;
2528 *type = REG_BINARY;
2529 if (strlenW(ptr)%2)
2530 *size = (strlenW(ptr)/2)+1;
2531 else
2532 *size = strlenW(ptr)/2;
2534 data = msi_alloc(*size);
2536 byte[0] = '0';
2537 byte[1] = 'x';
2538 byte[4] = 0;
2539 count = 0;
2540 /* if uneven pad with a zero in front */
2541 if (strlenW(ptr)%2)
2543 byte[2]= '0';
2544 byte[3]= *ptr;
2545 ptr++;
2546 data[count] = (BYTE)strtol(byte,NULL,0);
2547 count ++;
2548 TRACE("Uneven byte count\n");
2550 while (*ptr)
2552 byte[2]= *ptr;
2553 ptr++;
2554 byte[3]= *ptr;
2555 ptr++;
2556 data[count] = (BYTE)strtol(byte,NULL,0);
2557 count ++;
2559 msi_free(deformated);
2561 TRACE("Data %i bytes(%i)\n",*size,count);
2563 else
2565 LPWSTR deformated;
2566 LPWSTR p;
2567 DWORD d = 0;
2568 deformat_string(package, &value[1], &deformated);
2570 *type=REG_DWORD;
2571 *size = sizeof(DWORD);
2572 data = msi_alloc(*size);
2573 p = deformated;
2574 if (*p == '-')
2575 p++;
2576 while (*p)
2578 if ( (*p < '0') || (*p > '9') )
2579 break;
2580 d *= 10;
2581 d += (*p - '0');
2582 p++;
2584 if (deformated[0] == '-')
2585 d = -d;
2586 *(LPDWORD)data = d;
2587 TRACE("DWORD %i\n",*(LPDWORD)data);
2589 msi_free(deformated);
2592 else
2594 const WCHAR *ptr = value;
2596 *type = REG_SZ;
2597 if (value[0] == '#')
2599 ptr++; len--;
2600 if (value[1] == '%')
2602 ptr++; len--;
2603 *type = REG_EXPAND_SZ;
2606 data = (BYTE *)msi_strdupW( ptr, len );
2607 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2608 *size = (len + 1) * sizeof(WCHAR);
2610 return data;
2613 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2615 const WCHAR *ret;
2617 switch (root)
2619 case -1:
2620 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2622 *root_key = HKEY_LOCAL_MACHINE;
2623 ret = szHLM;
2625 else
2627 *root_key = HKEY_CURRENT_USER;
2628 ret = szHCU;
2630 break;
2631 case 0:
2632 *root_key = HKEY_CLASSES_ROOT;
2633 ret = szHCR;
2634 break;
2635 case 1:
2636 *root_key = HKEY_CURRENT_USER;
2637 ret = szHCU;
2638 break;
2639 case 2:
2640 *root_key = HKEY_LOCAL_MACHINE;
2641 ret = szHLM;
2642 break;
2643 case 3:
2644 *root_key = HKEY_USERS;
2645 ret = szHU;
2646 break;
2647 default:
2648 ERR("Unknown root %i\n", root);
2649 return NULL;
2652 return ret;
2655 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2657 REGSAM view = 0;
2658 if (is_wow64 || is_64bit)
2659 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2660 return view;
2663 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2665 WCHAR *subkey, *p, *q;
2666 HKEY hkey, ret = NULL;
2667 LONG res;
2669 access |= get_registry_view( comp );
2671 if (!(subkey = strdupW( path ))) return NULL;
2672 p = subkey;
2673 if ((q = strchrW( p, '\\' ))) *q = 0;
2674 if (create)
2675 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2676 else
2677 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2678 if (res)
2680 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2681 msi_free( subkey );
2682 return NULL;
2684 if (q && q[1])
2686 ret = open_key( comp, hkey, q + 1, create, access );
2687 RegCloseKey( hkey );
2689 else ret = hkey;
2690 msi_free( subkey );
2691 return ret;
2694 static BOOL is_special_entry( const WCHAR *name )
2696 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2699 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2701 const WCHAR *p = str;
2702 WCHAR **ret;
2703 int i = 0;
2705 *count = 0;
2706 if (!str) return NULL;
2707 while ((p - str) < len)
2709 p += strlenW( p ) + 1;
2710 (*count)++;
2712 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2713 p = str;
2714 while ((p - str) < len)
2716 if (!(ret[i] = strdupW( p )))
2718 for (; i >= 0; i--) msi_free( ret[i] );
2719 msi_free( ret );
2720 return NULL;
2722 p += strlenW( p ) + 1;
2723 i++;
2725 return ret;
2728 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2729 WCHAR **right, DWORD right_count, DWORD *size )
2731 WCHAR *ret, *p;
2732 unsigned int i;
2734 *size = sizeof(WCHAR);
2735 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2736 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2738 if (!(ret = p = msi_alloc( *size ))) return NULL;
2740 for (i = 0; i < left_count; i++)
2742 strcpyW( p, left[i] );
2743 p += strlenW( p ) + 1;
2745 for (i = 0; i < right_count; i++)
2747 strcpyW( p, right[i] );
2748 p += strlenW( p ) + 1;
2750 *p = 0;
2751 return ret;
2754 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2755 WCHAR **new, DWORD new_count )
2757 DWORD ret = old_count;
2758 unsigned int i, j, k;
2760 for (i = 0; i < new_count; i++)
2762 for (j = 0; j < old_count; j++)
2764 if (old[j] && !strcmpW( new[i], old[j] ))
2766 msi_free( old[j] );
2767 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2768 old[k] = NULL;
2769 ret--;
2773 return ret;
2776 enum join_op
2778 JOIN_OP_APPEND,
2779 JOIN_OP_PREPEND,
2780 JOIN_OP_REPLACE
2783 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2784 WCHAR **new, DWORD new_count, DWORD *size )
2786 switch (op)
2788 case JOIN_OP_APPEND:
2789 old_count = remove_duplicate_values( old, old_count, new, new_count );
2790 return flatten_multi_string_values( old, old_count, new, new_count, size );
2792 case JOIN_OP_PREPEND:
2793 old_count = remove_duplicate_values( old, old_count, new, new_count );
2794 return flatten_multi_string_values( new, new_count, old, old_count, size );
2796 case JOIN_OP_REPLACE:
2797 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2799 default:
2800 ERR("unhandled join op %u\n", op);
2801 return NULL;
2805 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2806 BYTE *new_value, DWORD new_size, DWORD *size )
2808 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2809 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2810 enum join_op op = JOIN_OP_REPLACE;
2811 WCHAR **old = NULL, **new = NULL;
2812 BYTE *ret;
2814 if (new_size / sizeof(WCHAR) - 1 > 1)
2816 new_ptr = (const WCHAR *)new_value;
2817 new_len = new_size / sizeof(WCHAR) - 1;
2819 if (!new_ptr[0] && new_ptr[new_len - 1])
2821 op = JOIN_OP_APPEND;
2822 new_len--;
2823 new_ptr++;
2825 else if (new_ptr[0] && !new_ptr[new_len - 1])
2827 op = JOIN_OP_PREPEND;
2828 new_len--;
2830 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2832 op = JOIN_OP_REPLACE;
2833 new_len -= 2;
2834 new_ptr++;
2836 new = split_multi_string_values( new_ptr, new_len, &new_count );
2838 if (old_size / sizeof(WCHAR) - 1 > 1)
2840 old_ptr = (const WCHAR *)old_value;
2841 old_len = old_size / sizeof(WCHAR) - 1;
2842 old = split_multi_string_values( old_ptr, old_len, &old_count );
2844 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2845 for (i = 0; i < old_count; i++) msi_free( old[i] );
2846 for (i = 0; i < new_count; i++) msi_free( new[i] );
2847 msi_free( old );
2848 msi_free( new );
2849 return ret;
2852 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2854 BYTE *ret;
2855 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2856 if (!(ret = msi_alloc( *size ))) return NULL;
2857 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2858 return ret;
2861 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2863 MSIPACKAGE *package = param;
2864 BYTE *new_value, *old_value = NULL;
2865 HKEY root_key, hkey;
2866 DWORD type, old_type, new_size, old_size = 0;
2867 LPWSTR deformated, uikey;
2868 const WCHAR *szRoot, *component, *name, *key, *str;
2869 MSICOMPONENT *comp;
2870 MSIRECORD * uirow;
2871 INT root;
2872 BOOL check_first = FALSE;
2873 int len;
2875 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2877 component = MSI_RecordGetString(row, 6);
2878 comp = msi_get_loaded_component(package,component);
2879 if (!comp)
2880 return ERROR_SUCCESS;
2882 comp->Action = msi_get_component_action( package, comp );
2883 if (comp->Action != INSTALLSTATE_LOCAL)
2885 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2886 return ERROR_SUCCESS;
2889 name = MSI_RecordGetString(row, 4);
2890 if( MSI_RecordIsNull(row,5) && name )
2892 /* null values can have special meanings */
2893 if (name[0]=='-' && name[1] == 0)
2894 return ERROR_SUCCESS;
2895 if ((name[0] == '+' || name[0] == '*') && !name[1])
2896 check_first = TRUE;
2899 root = MSI_RecordGetInteger(row,2);
2900 key = MSI_RecordGetString(row, 3);
2902 szRoot = get_root_key( package, root, &root_key );
2903 if (!szRoot)
2904 return ERROR_SUCCESS;
2906 deformat_string(package, key , &deformated);
2907 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2908 strcpyW(uikey,szRoot);
2909 strcatW(uikey,deformated);
2911 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2913 ERR("Could not create key %s\n", debugstr_w(deformated));
2914 msi_free(uikey);
2915 msi_free(deformated);
2916 return ERROR_FUNCTION_FAILED;
2918 msi_free( deformated );
2919 str = msi_record_get_string( row, 5, NULL );
2920 len = deformat_string( package, str, &deformated );
2921 new_value = parse_value( package, deformated, len, &type, &new_size );
2923 msi_free( deformated );
2924 deformat_string(package, name, &deformated);
2926 if (!is_special_entry( name ))
2928 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2929 if (type == REG_MULTI_SZ)
2931 BYTE *new;
2932 if (old_value && old_type != REG_MULTI_SZ)
2934 msi_free( old_value );
2935 old_value = NULL;
2936 old_size = 0;
2938 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2939 msi_free( new_value );
2940 new_value = new;
2942 if (!check_first)
2944 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2945 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2947 else if (!old_value)
2949 if (deformated || new_size)
2951 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2952 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2955 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2957 RegCloseKey(hkey);
2959 uirow = MSI_CreateRecord(3);
2960 MSI_RecordSetStringW(uirow,2,deformated);
2961 MSI_RecordSetStringW(uirow,1,uikey);
2962 if (type == REG_SZ || type == REG_EXPAND_SZ)
2963 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2964 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2965 msiobj_release( &uirow->hdr );
2967 msi_free(new_value);
2968 msi_free(old_value);
2969 msi_free(deformated);
2970 msi_free(uikey);
2972 return ERROR_SUCCESS;
2975 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2977 static const WCHAR query[] = {
2978 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2979 '`','R','e','g','i','s','t','r','y','`',0};
2980 MSIQUERY *view;
2981 UINT rc;
2983 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2984 if (rc != ERROR_SUCCESS)
2985 return ERROR_SUCCESS;
2987 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2988 msiobj_release(&view->hdr);
2989 return rc;
2992 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2994 REGSAM access = 0;
2995 WCHAR *subkey, *p;
2996 HKEY hkey;
2997 LONG res;
2999 access |= get_registry_view( comp );
3001 if (!(subkey = strdupW( path ))) return;
3004 if ((p = strrchrW( subkey, '\\' )))
3006 *p = 0;
3007 if (!p[1]) continue; /* trailing backslash */
3008 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3009 if (!hkey) break;
3010 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3011 RegCloseKey( hkey );
3013 else
3014 res = RegDeleteKeyExW( root, subkey, access, 0 );
3015 if (res)
3017 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3018 break;
3020 } while (p);
3021 msi_free( subkey );
3024 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3026 LONG res;
3027 HKEY hkey;
3028 DWORD num_subkeys, num_values;
3030 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3032 if ((res = RegDeleteValueW( hkey, value )))
3033 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3035 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3036 NULL, NULL, NULL, NULL );
3037 RegCloseKey( hkey );
3038 if (!res && !num_subkeys && !num_values)
3040 TRACE("removing empty key %s\n", debugstr_w(path));
3041 delete_key( comp, root, path );
3046 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3048 LONG res;
3049 HKEY hkey;
3051 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3052 res = RegDeleteTreeW( hkey, NULL );
3053 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3054 delete_key( comp, root, path );
3055 RegCloseKey( hkey );
3058 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3060 MSIPACKAGE *package = param;
3061 LPCWSTR component, name, key_str, root_key_str;
3062 LPWSTR deformated_key, deformated_name, ui_key_str;
3063 MSICOMPONENT *comp;
3064 MSIRECORD *uirow;
3065 BOOL delete_key = FALSE;
3066 HKEY hkey_root;
3067 UINT size;
3068 INT root;
3070 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3072 component = MSI_RecordGetString( row, 6 );
3073 comp = msi_get_loaded_component( package, component );
3074 if (!comp)
3075 return ERROR_SUCCESS;
3077 comp->Action = msi_get_component_action( package, comp );
3078 if (comp->Action != INSTALLSTATE_ABSENT)
3080 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3081 return ERROR_SUCCESS;
3084 name = MSI_RecordGetString( row, 4 );
3085 if (MSI_RecordIsNull( row, 5 ) && name )
3087 if (name[0] == '+' && !name[1])
3088 return ERROR_SUCCESS;
3089 if ((name[0] == '-' || name[0] == '*') && !name[1])
3091 delete_key = TRUE;
3092 name = NULL;
3096 root = MSI_RecordGetInteger( row, 2 );
3097 key_str = MSI_RecordGetString( row, 3 );
3099 root_key_str = get_root_key( package, root, &hkey_root );
3100 if (!root_key_str)
3101 return ERROR_SUCCESS;
3103 deformat_string( package, key_str, &deformated_key );
3104 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3105 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3106 strcpyW( ui_key_str, root_key_str );
3107 strcatW( ui_key_str, deformated_key );
3109 deformat_string( package, name, &deformated_name );
3111 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3112 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3113 msi_free( deformated_key );
3115 uirow = MSI_CreateRecord( 2 );
3116 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3117 MSI_RecordSetStringW( uirow, 2, deformated_name );
3118 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3119 msiobj_release( &uirow->hdr );
3121 msi_free( ui_key_str );
3122 msi_free( deformated_name );
3123 return ERROR_SUCCESS;
3126 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3128 MSIPACKAGE *package = param;
3129 LPCWSTR component, name, key_str, root_key_str;
3130 LPWSTR deformated_key, deformated_name, ui_key_str;
3131 MSICOMPONENT *comp;
3132 MSIRECORD *uirow;
3133 BOOL delete_key = FALSE;
3134 HKEY hkey_root;
3135 UINT size;
3136 INT root;
3138 component = MSI_RecordGetString( row, 5 );
3139 comp = msi_get_loaded_component( package, component );
3140 if (!comp)
3141 return ERROR_SUCCESS;
3143 comp->Action = msi_get_component_action( package, comp );
3144 if (comp->Action != INSTALLSTATE_LOCAL)
3146 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3147 return ERROR_SUCCESS;
3150 if ((name = MSI_RecordGetString( row, 4 )))
3152 if (name[0] == '-' && !name[1])
3154 delete_key = TRUE;
3155 name = NULL;
3159 root = MSI_RecordGetInteger( row, 2 );
3160 key_str = MSI_RecordGetString( row, 3 );
3162 root_key_str = get_root_key( package, root, &hkey_root );
3163 if (!root_key_str)
3164 return ERROR_SUCCESS;
3166 deformat_string( package, key_str, &deformated_key );
3167 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3168 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3169 strcpyW( ui_key_str, root_key_str );
3170 strcatW( ui_key_str, deformated_key );
3172 deformat_string( package, name, &deformated_name );
3174 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3175 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3176 msi_free( deformated_key );
3178 uirow = MSI_CreateRecord( 2 );
3179 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3180 MSI_RecordSetStringW( uirow, 2, deformated_name );
3181 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3182 msiobj_release( &uirow->hdr );
3184 msi_free( ui_key_str );
3185 msi_free( deformated_name );
3186 return ERROR_SUCCESS;
3189 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3191 static const WCHAR registry_query[] = {
3192 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3193 '`','R','e','g','i','s','t','r','y','`',0};
3194 static const WCHAR remove_registry_query[] = {
3195 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3196 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3197 MSIQUERY *view;
3198 UINT rc;
3200 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3201 if (rc == ERROR_SUCCESS)
3203 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3204 msiobj_release( &view->hdr );
3205 if (rc != ERROR_SUCCESS)
3206 return rc;
3208 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3209 if (rc == ERROR_SUCCESS)
3211 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3212 msiobj_release( &view->hdr );
3213 if (rc != ERROR_SUCCESS)
3214 return rc;
3216 return ERROR_SUCCESS;
3219 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3221 return ERROR_SUCCESS;
3225 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3227 static const WCHAR query[]= {
3228 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3229 '`','R','e','g','i','s','t','r','y','`',0};
3230 MSICOMPONENT *comp;
3231 DWORD total = 0, count = 0;
3232 MSIQUERY *view;
3233 MSIFEATURE *feature;
3234 MSIFILE *file;
3235 UINT rc;
3237 TRACE("InstallValidate\n");
3239 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3240 if (rc == ERROR_SUCCESS)
3242 rc = MSI_IterateRecords( view, &count, NULL, package );
3243 msiobj_release( &view->hdr );
3244 if (rc != ERROR_SUCCESS)
3245 return rc;
3246 total += count * REG_PROGRESS_VALUE;
3248 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3249 total += COMPONENT_PROGRESS_VALUE;
3251 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3252 total += file->FileSize;
3254 msi_ui_progress( package, 0, total, 0, 0 );
3256 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3258 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3259 debugstr_w(feature->Feature), feature->Installed,
3260 feature->ActionRequest, feature->Action);
3262 return ERROR_SUCCESS;
3265 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3267 MSIPACKAGE* package = param;
3268 LPCWSTR cond = NULL;
3269 LPCWSTR message = NULL;
3270 UINT r;
3272 static const WCHAR title[]=
3273 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3275 cond = MSI_RecordGetString(row,1);
3277 r = MSI_EvaluateConditionW(package,cond);
3278 if (r == MSICONDITION_FALSE)
3280 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3282 LPWSTR deformated;
3283 message = MSI_RecordGetString(row,2);
3284 deformat_string(package,message,&deformated);
3285 MessageBoxW(NULL,deformated,title,MB_OK);
3286 msi_free(deformated);
3289 return ERROR_INSTALL_FAILURE;
3292 return ERROR_SUCCESS;
3295 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3297 static const WCHAR query[] = {
3298 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3299 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3300 MSIQUERY *view;
3301 UINT rc;
3303 TRACE("Checking launch conditions\n");
3305 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3306 if (rc != ERROR_SUCCESS)
3307 return ERROR_SUCCESS;
3309 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3310 msiobj_release(&view->hdr);
3311 return rc;
3314 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3317 if (!cmp->KeyPath)
3318 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3320 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3322 static const WCHAR query[] = {
3323 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3324 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3325 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3326 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3327 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3328 MSIRECORD *row;
3329 UINT root, len;
3330 LPWSTR deformated, buffer, deformated_name;
3331 LPCWSTR key, name;
3333 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3334 if (!row)
3335 return NULL;
3337 root = MSI_RecordGetInteger(row,2);
3338 key = MSI_RecordGetString(row, 3);
3339 name = MSI_RecordGetString(row, 4);
3340 deformat_string(package, key , &deformated);
3341 deformat_string(package, name, &deformated_name);
3343 len = strlenW(deformated) + 6;
3344 if (deformated_name)
3345 len+=strlenW(deformated_name);
3347 buffer = msi_alloc( len *sizeof(WCHAR));
3349 if (deformated_name)
3350 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3351 else
3352 sprintfW(buffer,fmt,root,deformated);
3354 msi_free(deformated);
3355 msi_free(deformated_name);
3356 msiobj_release(&row->hdr);
3358 return buffer;
3360 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3362 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3363 return NULL;
3365 else
3367 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3369 if (file)
3370 return strdupW( file->TargetPath );
3372 return NULL;
3375 static HKEY openSharedDLLsKey(void)
3377 HKEY hkey=0;
3378 static const WCHAR path[] =
3379 {'S','o','f','t','w','a','r','e','\\',
3380 'M','i','c','r','o','s','o','f','t','\\',
3381 'W','i','n','d','o','w','s','\\',
3382 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3383 'S','h','a','r','e','d','D','L','L','s',0};
3385 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3386 return hkey;
3389 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3391 HKEY hkey;
3392 DWORD count=0;
3393 DWORD type;
3394 DWORD sz = sizeof(count);
3395 DWORD rc;
3397 hkey = openSharedDLLsKey();
3398 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3399 if (rc != ERROR_SUCCESS)
3400 count = 0;
3401 RegCloseKey(hkey);
3402 return count;
3405 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3407 HKEY hkey;
3409 hkey = openSharedDLLsKey();
3410 if (count > 0)
3411 msi_reg_set_val_dword( hkey, path, count );
3412 else
3413 RegDeleteValueW(hkey,path);
3414 RegCloseKey(hkey);
3415 return count;
3418 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3420 MSIFEATURE *feature;
3421 INT count = 0;
3422 BOOL write = FALSE;
3424 /* only refcount DLLs */
3425 if (comp->KeyPath == NULL ||
3426 comp->assembly ||
3427 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3428 comp->Attributes & msidbComponentAttributesODBCDataSource)
3429 write = FALSE;
3430 else
3432 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3433 write = (count > 0);
3435 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3436 write = TRUE;
3439 /* increment counts */
3440 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3442 ComponentList *cl;
3444 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3445 continue;
3447 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3449 if ( cl->component == comp )
3450 count++;
3454 /* decrement counts */
3455 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3457 ComponentList *cl;
3459 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3460 continue;
3462 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3464 if ( cl->component == comp )
3465 count--;
3469 /* ref count all the files in the component */
3470 if (write)
3472 MSIFILE *file;
3474 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3476 if (file->Component == comp)
3477 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3481 /* add a count for permanent */
3482 if (comp->Attributes & msidbComponentAttributesPermanent)
3483 count ++;
3485 comp->RefCount = count;
3487 if (write)
3488 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3491 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3493 if (comp->assembly)
3495 const WCHAR prefixW[] = {'<','\\',0};
3496 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3497 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3499 if (keypath)
3501 strcpyW( keypath, prefixW );
3502 strcatW( keypath, comp->assembly->display_name );
3504 return keypath;
3506 return resolve_keypath( package, comp );
3509 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3511 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3512 UINT rc;
3513 MSICOMPONENT *comp;
3514 HKEY hkey;
3516 TRACE("\n");
3518 squash_guid( package->ProductCode, squashed_pc );
3519 msi_set_sourcedir_props(package, FALSE);
3521 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3523 MSIRECORD *uirow;
3524 INSTALLSTATE action;
3526 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3527 if (!comp->ComponentId)
3528 continue;
3530 squash_guid( comp->ComponentId, squashed_cc );
3531 msi_free( comp->FullKeypath );
3532 comp->FullKeypath = build_full_keypath( package, comp );
3534 ACTION_RefCountComponent( package, comp );
3536 if (package->need_rollback) action = comp->Installed;
3537 else action = comp->ActionRequest;
3539 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3540 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3541 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3543 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3545 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3546 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3547 else
3548 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3550 if (rc != ERROR_SUCCESS)
3551 continue;
3553 if (comp->Attributes & msidbComponentAttributesPermanent)
3555 static const WCHAR szPermKey[] =
3556 { '0','0','0','0','0','0','0','0','0','0','0','0',
3557 '0','0','0','0','0','0','0','0','0','0','0','0',
3558 '0','0','0','0','0','0','0','0',0 };
3560 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3562 if (action == INSTALLSTATE_LOCAL)
3563 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3564 else
3566 MSIFILE *file;
3567 MSIRECORD *row;
3568 LPWSTR ptr, ptr2;
3569 WCHAR source[MAX_PATH];
3570 WCHAR base[MAX_PATH];
3571 LPWSTR sourcepath;
3573 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3574 static const WCHAR query[] = {
3575 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3576 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3577 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3578 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3579 '`','D','i','s','k','I','d','`',0};
3581 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3582 continue;
3584 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3585 return ERROR_FUNCTION_FAILED;
3587 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3588 ptr2 = strrchrW(source, '\\') + 1;
3589 msiobj_release(&row->hdr);
3591 lstrcpyW(base, package->PackagePath);
3592 ptr = strrchrW(base, '\\');
3593 *(ptr + 1) = '\0';
3595 sourcepath = msi_resolve_file_source(package, file);
3596 ptr = sourcepath + lstrlenW(base);
3597 lstrcpyW(ptr2, ptr);
3598 msi_free(sourcepath);
3600 msi_reg_set_val_str( hkey, squashed_pc, source );
3602 RegCloseKey(hkey);
3604 else if (action == INSTALLSTATE_ABSENT)
3606 if (comp->num_clients <= 0)
3608 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3609 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3610 else
3611 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3613 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3615 else
3617 LONG res;
3619 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3620 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3621 else
3622 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3624 if (rc != ERROR_SUCCESS)
3626 WARN( "failed to open component key %u\n", rc );
3627 continue;
3629 res = RegDeleteValueW( hkey, squashed_pc );
3630 RegCloseKey(hkey);
3631 if (res) WARN( "failed to delete component value %d\n", res );
3635 /* UI stuff */
3636 uirow = MSI_CreateRecord(3);
3637 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3638 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3639 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3640 msi_ui_actiondata( package, szProcessComponents, uirow );
3641 msiobj_release( &uirow->hdr );
3643 return ERROR_SUCCESS;
3646 typedef struct {
3647 CLSID clsid;
3648 LPWSTR source;
3650 LPWSTR path;
3651 ITypeLib *ptLib;
3652 } typelib_struct;
3654 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3655 LPWSTR lpszName, LONG_PTR lParam)
3657 TLIBATTR *attr;
3658 typelib_struct *tl_struct = (typelib_struct*) lParam;
3659 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3660 int sz;
3661 HRESULT res;
3663 if (!IS_INTRESOURCE(lpszName))
3665 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3666 return TRUE;
3669 sz = strlenW(tl_struct->source)+4;
3670 sz *= sizeof(WCHAR);
3672 if ((INT_PTR)lpszName == 1)
3673 tl_struct->path = strdupW(tl_struct->source);
3674 else
3676 tl_struct->path = msi_alloc(sz);
3677 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3680 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3681 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3682 if (FAILED(res))
3684 msi_free(tl_struct->path);
3685 tl_struct->path = NULL;
3687 return TRUE;
3690 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3691 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3693 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3694 return FALSE;
3697 msi_free(tl_struct->path);
3698 tl_struct->path = NULL;
3700 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3701 ITypeLib_Release(tl_struct->ptLib);
3703 return TRUE;
3706 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3708 MSIPACKAGE* package = param;
3709 LPCWSTR component;
3710 MSICOMPONENT *comp;
3711 MSIFILE *file;
3712 typelib_struct tl_struct;
3713 ITypeLib *tlib;
3714 HMODULE module;
3715 HRESULT hr;
3717 component = MSI_RecordGetString(row,3);
3718 comp = msi_get_loaded_component(package,component);
3719 if (!comp)
3720 return ERROR_SUCCESS;
3722 comp->Action = msi_get_component_action( package, comp );
3723 if (comp->Action != INSTALLSTATE_LOCAL)
3725 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3726 return ERROR_SUCCESS;
3729 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3731 TRACE("component has no key path\n");
3732 return ERROR_SUCCESS;
3734 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3736 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3737 if (module)
3739 LPCWSTR guid;
3740 guid = MSI_RecordGetString(row,1);
3741 CLSIDFromString( guid, &tl_struct.clsid);
3742 tl_struct.source = strdupW( file->TargetPath );
3743 tl_struct.path = NULL;
3745 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3746 (LONG_PTR)&tl_struct);
3748 if (tl_struct.path)
3750 LPCWSTR helpid, help_path = NULL;
3751 HRESULT res;
3753 helpid = MSI_RecordGetString(row,6);
3755 if (helpid) help_path = msi_get_target_folder( package, helpid );
3756 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3758 if (FAILED(res))
3759 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3760 else
3761 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3763 ITypeLib_Release(tl_struct.ptLib);
3764 msi_free(tl_struct.path);
3766 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3768 FreeLibrary(module);
3769 msi_free(tl_struct.source);
3771 else
3773 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3774 if (FAILED(hr))
3776 ERR("Failed to load type library: %08x\n", hr);
3777 return ERROR_INSTALL_FAILURE;
3780 ITypeLib_Release(tlib);
3783 return ERROR_SUCCESS;
3786 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3788 static const WCHAR query[] = {
3789 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3790 '`','T','y','p','e','L','i','b','`',0};
3791 MSIQUERY *view;
3792 UINT rc;
3794 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3795 if (rc != ERROR_SUCCESS)
3796 return ERROR_SUCCESS;
3798 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3799 msiobj_release(&view->hdr);
3800 return rc;
3803 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3805 MSIPACKAGE *package = param;
3806 LPCWSTR component, guid;
3807 MSICOMPONENT *comp;
3808 GUID libid;
3809 UINT version;
3810 LCID language;
3811 SYSKIND syskind;
3812 HRESULT hr;
3814 component = MSI_RecordGetString( row, 3 );
3815 comp = msi_get_loaded_component( package, component );
3816 if (!comp)
3817 return ERROR_SUCCESS;
3819 comp->Action = msi_get_component_action( package, comp );
3820 if (comp->Action != INSTALLSTATE_ABSENT)
3822 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3823 return ERROR_SUCCESS;
3825 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3827 guid = MSI_RecordGetString( row, 1 );
3828 CLSIDFromString( guid, &libid );
3829 version = MSI_RecordGetInteger( row, 4 );
3830 language = MSI_RecordGetInteger( row, 2 );
3832 #ifdef _WIN64
3833 syskind = SYS_WIN64;
3834 #else
3835 syskind = SYS_WIN32;
3836 #endif
3838 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3839 if (FAILED(hr))
3841 WARN("Failed to unregister typelib: %08x\n", hr);
3844 return ERROR_SUCCESS;
3847 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3849 static const WCHAR query[] = {
3850 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3851 '`','T','y','p','e','L','i','b','`',0};
3852 MSIQUERY *view;
3853 UINT rc;
3855 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3856 if (rc != ERROR_SUCCESS)
3857 return ERROR_SUCCESS;
3859 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3860 msiobj_release( &view->hdr );
3861 return rc;
3864 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3866 static const WCHAR szlnk[] = {'.','l','n','k',0};
3867 LPCWSTR directory, extension, link_folder;
3868 LPWSTR link_file, filename;
3870 directory = MSI_RecordGetString( row, 2 );
3871 link_folder = msi_get_target_folder( package, directory );
3872 if (!link_folder)
3874 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3875 return NULL;
3877 /* may be needed because of a bug somewhere else */
3878 msi_create_full_path( link_folder );
3880 filename = msi_dup_record_field( row, 3 );
3881 msi_reduce_to_long_filename( filename );
3883 extension = strrchrW( filename, '.' );
3884 if (!extension || strcmpiW( extension, szlnk ))
3886 int len = strlenW( filename );
3887 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3888 memcpy( filename + len, szlnk, sizeof(szlnk) );
3890 link_file = msi_build_directory_name( 2, link_folder, filename );
3891 msi_free( filename );
3893 return link_file;
3896 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3898 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3899 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3900 WCHAR *folder, *dest, *path;
3902 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3903 folder = msi_dup_property( package->db, szWindowsFolder );
3904 else
3906 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3907 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3908 msi_free( appdata );
3910 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3911 msi_create_full_path( dest );
3912 path = msi_build_directory_name( 2, dest, icon_name );
3913 msi_free( folder );
3914 msi_free( dest );
3915 return path;
3918 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3920 MSIPACKAGE *package = param;
3921 LPWSTR link_file, deformated, path;
3922 LPCWSTR component, target;
3923 MSICOMPONENT *comp;
3924 IShellLinkW *sl = NULL;
3925 IPersistFile *pf = NULL;
3926 HRESULT res;
3928 component = MSI_RecordGetString(row, 4);
3929 comp = msi_get_loaded_component(package, component);
3930 if (!comp)
3931 return ERROR_SUCCESS;
3933 comp->Action = msi_get_component_action( package, comp );
3934 if (comp->Action != INSTALLSTATE_LOCAL)
3936 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3937 return ERROR_SUCCESS;
3939 msi_ui_actiondata( package, szCreateShortcuts, row );
3941 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3942 &IID_IShellLinkW, (LPVOID *) &sl );
3944 if (FAILED( res ))
3946 ERR("CLSID_ShellLink not available\n");
3947 goto err;
3950 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3951 if (FAILED( res ))
3953 ERR("QueryInterface(IID_IPersistFile) failed\n");
3954 goto err;
3957 target = MSI_RecordGetString(row, 5);
3958 if (strchrW(target, '['))
3960 deformat_string( package, target, &path );
3961 TRACE("target path is %s\n", debugstr_w(path));
3962 IShellLinkW_SetPath( sl, path );
3963 msi_free( path );
3965 else
3967 FIXME("poorly handled shortcut format, advertised shortcut\n");
3968 path = resolve_keypath( package, comp );
3969 IShellLinkW_SetPath( sl, path );
3970 msi_free( path );
3973 if (!MSI_RecordIsNull(row,6))
3975 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3976 deformat_string(package, arguments, &deformated);
3977 IShellLinkW_SetArguments(sl,deformated);
3978 msi_free(deformated);
3981 if (!MSI_RecordIsNull(row,7))
3983 LPCWSTR description = MSI_RecordGetString(row, 7);
3984 IShellLinkW_SetDescription(sl, description);
3987 if (!MSI_RecordIsNull(row,8))
3988 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3990 if (!MSI_RecordIsNull(row,9))
3992 INT index;
3993 LPCWSTR icon = MSI_RecordGetString(row, 9);
3995 path = msi_build_icon_path(package, icon);
3996 index = MSI_RecordGetInteger(row,10);
3998 /* no value means 0 */
3999 if (index == MSI_NULL_INTEGER)
4000 index = 0;
4002 IShellLinkW_SetIconLocation(sl, path, index);
4003 msi_free(path);
4006 if (!MSI_RecordIsNull(row,11))
4007 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4009 if (!MSI_RecordIsNull(row,12))
4011 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4012 full_path = msi_get_target_folder( package, wkdir );
4013 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4015 link_file = get_link_file(package, row);
4017 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4018 IPersistFile_Save(pf, link_file, FALSE);
4019 msi_free(link_file);
4021 err:
4022 if (pf)
4023 IPersistFile_Release( pf );
4024 if (sl)
4025 IShellLinkW_Release( sl );
4027 return ERROR_SUCCESS;
4030 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4032 static const WCHAR query[] = {
4033 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4034 '`','S','h','o','r','t','c','u','t','`',0};
4035 MSIQUERY *view;
4036 HRESULT res;
4037 UINT rc;
4039 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4040 if (rc != ERROR_SUCCESS)
4041 return ERROR_SUCCESS;
4043 res = CoInitialize( NULL );
4045 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4046 msiobj_release(&view->hdr);
4048 if (SUCCEEDED(res)) CoUninitialize();
4049 return rc;
4052 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4054 MSIPACKAGE *package = param;
4055 LPWSTR link_file;
4056 LPCWSTR component;
4057 MSICOMPONENT *comp;
4059 component = MSI_RecordGetString( row, 4 );
4060 comp = msi_get_loaded_component( package, component );
4061 if (!comp)
4062 return ERROR_SUCCESS;
4064 comp->Action = msi_get_component_action( package, comp );
4065 if (comp->Action != INSTALLSTATE_ABSENT)
4067 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4068 return ERROR_SUCCESS;
4070 msi_ui_actiondata( package, szRemoveShortcuts, row );
4072 link_file = get_link_file( package, row );
4074 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4075 if (!DeleteFileW( link_file ))
4077 WARN("Failed to remove shortcut file %u\n", GetLastError());
4079 msi_free( link_file );
4081 return ERROR_SUCCESS;
4084 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4086 static const WCHAR query[] = {
4087 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4088 '`','S','h','o','r','t','c','u','t','`',0};
4089 MSIQUERY *view;
4090 UINT rc;
4092 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4093 if (rc != ERROR_SUCCESS)
4094 return ERROR_SUCCESS;
4096 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4097 msiobj_release( &view->hdr );
4098 return rc;
4101 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4103 MSIPACKAGE* package = param;
4104 HANDLE the_file;
4105 LPWSTR FilePath;
4106 LPCWSTR FileName;
4107 CHAR buffer[1024];
4108 DWORD sz;
4109 UINT rc;
4111 FileName = MSI_RecordGetString(row,1);
4112 if (!FileName)
4114 ERR("Unable to get FileName\n");
4115 return ERROR_SUCCESS;
4118 FilePath = msi_build_icon_path(package, FileName);
4120 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4122 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4123 FILE_ATTRIBUTE_NORMAL, NULL);
4125 if (the_file == INVALID_HANDLE_VALUE)
4127 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4128 msi_free(FilePath);
4129 return ERROR_SUCCESS;
4134 DWORD write;
4135 sz = 1024;
4136 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4137 if (rc != ERROR_SUCCESS)
4139 ERR("Failed to get stream\n");
4140 DeleteFileW(FilePath);
4141 break;
4143 WriteFile(the_file,buffer,sz,&write,NULL);
4144 } while (sz == 1024);
4146 msi_free(FilePath);
4147 CloseHandle(the_file);
4149 return ERROR_SUCCESS;
4152 static UINT msi_publish_icons(MSIPACKAGE *package)
4154 static const WCHAR query[]= {
4155 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4156 '`','I','c','o','n','`',0};
4157 MSIQUERY *view;
4158 UINT r;
4160 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4161 if (r == ERROR_SUCCESS)
4163 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4164 msiobj_release(&view->hdr);
4165 if (r != ERROR_SUCCESS)
4166 return r;
4168 return ERROR_SUCCESS;
4171 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4173 UINT r;
4174 HKEY source;
4175 LPWSTR buffer;
4176 MSIMEDIADISK *disk;
4177 MSISOURCELISTINFO *info;
4179 r = RegCreateKeyW(hkey, szSourceList, &source);
4180 if (r != ERROR_SUCCESS)
4181 return r;
4183 RegCloseKey(source);
4185 buffer = strrchrW(package->PackagePath, '\\') + 1;
4186 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4187 package->Context, MSICODE_PRODUCT,
4188 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4189 if (r != ERROR_SUCCESS)
4190 return r;
4192 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4193 package->Context, MSICODE_PRODUCT,
4194 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4195 if (r != ERROR_SUCCESS)
4196 return r;
4198 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4199 package->Context, MSICODE_PRODUCT,
4200 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4201 if (r != ERROR_SUCCESS)
4202 return r;
4204 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4206 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4207 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4208 info->options, info->value);
4209 else
4210 MsiSourceListSetInfoW(package->ProductCode, NULL,
4211 info->context, info->options,
4212 info->property, info->value);
4215 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4217 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4218 disk->context, disk->options,
4219 disk->disk_id, disk->volume_label, disk->disk_prompt);
4222 return ERROR_SUCCESS;
4225 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4227 static const WCHAR szARPProductIcon[] =
4228 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4229 static const WCHAR szAssignment[] =
4230 {'A','s','s','i','g','n','m','e','n','t',0};
4231 static const WCHAR szAdvertiseFlags[] =
4232 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4233 static const WCHAR szClients[] =
4234 {'C','l','i','e','n','t','s',0};
4235 static const WCHAR szColon[] = {':',0};
4236 MSIHANDLE hdb, suminfo;
4237 WCHAR *buffer, *ptr, guids[MAX_PATH], packcode[SQUASHED_GUID_SIZE];
4238 DWORD langid, size;
4239 UINT r;
4241 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4242 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4243 msi_free(buffer);
4245 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4246 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4248 /* FIXME */
4249 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4251 buffer = msi_dup_property(package->db, szARPProductIcon);
4252 if (buffer)
4254 LPWSTR path = msi_build_icon_path(package, buffer);
4255 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4256 msi_free(path);
4257 msi_free(buffer);
4260 buffer = msi_dup_property(package->db, szProductVersion);
4261 if (buffer)
4263 DWORD verdword = msi_version_str_to_dword(buffer);
4264 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4265 msi_free(buffer);
4268 msi_reg_set_val_dword(hkey, szAssignment, 0);
4269 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4270 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4271 msi_reg_set_val_str(hkey, szClients, szColon);
4273 hdb = alloc_msihandle(&package->db->hdr);
4274 if (!hdb)
4275 return ERROR_NOT_ENOUGH_MEMORY;
4277 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4278 MsiCloseHandle(hdb);
4279 if (r != ERROR_SUCCESS)
4280 goto done;
4282 size = MAX_PATH;
4283 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4284 NULL, guids, &size);
4285 if (r != ERROR_SUCCESS)
4286 goto done;
4288 ptr = strchrW(guids, ';');
4289 if (ptr) *ptr = 0;
4290 squash_guid(guids, packcode);
4291 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4293 done:
4294 MsiCloseHandle(suminfo);
4295 return ERROR_SUCCESS;
4298 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4300 UINT r;
4301 HKEY hkey;
4302 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4304 upgrade = msi_dup_property(package->db, szUpgradeCode);
4305 if (!upgrade)
4306 return ERROR_SUCCESS;
4308 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4309 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4310 else
4311 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4313 if (r != ERROR_SUCCESS)
4315 WARN("failed to open upgrade code key\n");
4316 msi_free(upgrade);
4317 return ERROR_SUCCESS;
4319 squash_guid(package->ProductCode, squashed_pc);
4320 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4321 RegCloseKey(hkey);
4322 msi_free(upgrade);
4323 return ERROR_SUCCESS;
4326 static BOOL msi_check_publish(MSIPACKAGE *package)
4328 MSIFEATURE *feature;
4330 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4332 feature->Action = msi_get_feature_action( package, feature );
4333 if (feature->Action == INSTALLSTATE_LOCAL)
4334 return TRUE;
4337 return FALSE;
4340 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4342 MSIFEATURE *feature;
4344 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4346 feature->Action = msi_get_feature_action( package, feature );
4347 if (feature->Action != INSTALLSTATE_ABSENT)
4348 return FALSE;
4351 return TRUE;
4354 static UINT msi_publish_patches( MSIPACKAGE *package )
4356 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4357 WCHAR patch_squashed[GUID_SIZE];
4358 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4359 LONG res;
4360 MSIPATCHINFO *patch;
4361 UINT r;
4362 WCHAR *p, *all_patches = NULL;
4363 DWORD len = 0;
4365 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4366 if (r != ERROR_SUCCESS)
4367 return ERROR_FUNCTION_FAILED;
4369 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4370 if (res != ERROR_SUCCESS)
4372 r = ERROR_FUNCTION_FAILED;
4373 goto done;
4376 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4377 if (r != ERROR_SUCCESS)
4378 goto done;
4380 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4382 squash_guid( patch->patchcode, patch_squashed );
4383 len += strlenW( patch_squashed ) + 1;
4386 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4387 if (!all_patches)
4388 goto done;
4390 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4392 HKEY patch_key;
4394 squash_guid( patch->patchcode, p );
4395 p += strlenW( p ) + 1;
4397 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4398 (const BYTE *)patch->transforms,
4399 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4400 if (res != ERROR_SUCCESS)
4401 goto done;
4403 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4404 if (r != ERROR_SUCCESS)
4405 goto done;
4407 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4408 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4409 RegCloseKey( patch_key );
4410 if (res != ERROR_SUCCESS)
4411 goto done;
4413 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4415 res = GetLastError();
4416 ERR("Unable to copy patch package %d\n", res);
4417 goto done;
4419 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4420 if (res != ERROR_SUCCESS)
4421 goto done;
4423 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4424 RegCloseKey( patch_key );
4425 if (res != ERROR_SUCCESS)
4426 goto done;
4429 all_patches[len] = 0;
4430 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4431 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4432 if (res != ERROR_SUCCESS)
4433 goto done;
4435 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4436 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4437 if (res != ERROR_SUCCESS)
4438 r = ERROR_FUNCTION_FAILED;
4440 done:
4441 RegCloseKey( product_patches_key );
4442 RegCloseKey( patches_key );
4443 RegCloseKey( product_key );
4444 msi_free( all_patches );
4445 return r;
4448 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4450 UINT rc;
4451 HKEY hukey = NULL, hudkey = NULL;
4452 MSIRECORD *uirow;
4454 if (!list_empty(&package->patches))
4456 rc = msi_publish_patches(package);
4457 if (rc != ERROR_SUCCESS)
4458 goto end;
4461 /* FIXME: also need to publish if the product is in advertise mode */
4462 if (!msi_check_publish(package))
4463 return ERROR_SUCCESS;
4465 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4466 &hukey, TRUE);
4467 if (rc != ERROR_SUCCESS)
4468 goto end;
4470 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4471 NULL, &hudkey, TRUE);
4472 if (rc != ERROR_SUCCESS)
4473 goto end;
4475 rc = msi_publish_upgrade_code(package);
4476 if (rc != ERROR_SUCCESS)
4477 goto end;
4479 rc = msi_publish_product_properties(package, hukey);
4480 if (rc != ERROR_SUCCESS)
4481 goto end;
4483 rc = msi_publish_sourcelist(package, hukey);
4484 if (rc != ERROR_SUCCESS)
4485 goto end;
4487 rc = msi_publish_icons(package);
4489 end:
4490 uirow = MSI_CreateRecord( 1 );
4491 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4492 msi_ui_actiondata( package, szPublishProduct, uirow );
4493 msiobj_release( &uirow->hdr );
4495 RegCloseKey(hukey);
4496 RegCloseKey(hudkey);
4497 return rc;
4500 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4502 WCHAR *filename, *ptr, *folder, *ret;
4503 const WCHAR *dirprop;
4505 filename = msi_dup_record_field( row, 2 );
4506 if (filename && (ptr = strchrW( filename, '|' )))
4507 ptr++;
4508 else
4509 ptr = filename;
4511 dirprop = MSI_RecordGetString( row, 3 );
4512 if (dirprop)
4514 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4515 if (!folder) folder = msi_dup_property( package->db, dirprop );
4517 else
4518 folder = msi_dup_property( package->db, szWindowsFolder );
4520 if (!folder)
4522 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4523 msi_free( filename );
4524 return NULL;
4527 ret = msi_build_directory_name( 2, folder, ptr );
4529 msi_free( filename );
4530 msi_free( folder );
4531 return ret;
4534 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4536 MSIPACKAGE *package = param;
4537 LPCWSTR component, section, key, value, identifier;
4538 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4539 MSIRECORD * uirow;
4540 INT action;
4541 MSICOMPONENT *comp;
4543 component = MSI_RecordGetString(row, 8);
4544 comp = msi_get_loaded_component(package,component);
4545 if (!comp)
4546 return ERROR_SUCCESS;
4548 comp->Action = msi_get_component_action( package, comp );
4549 if (comp->Action != INSTALLSTATE_LOCAL)
4551 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4552 return ERROR_SUCCESS;
4555 identifier = MSI_RecordGetString(row,1);
4556 section = MSI_RecordGetString(row,4);
4557 key = MSI_RecordGetString(row,5);
4558 value = MSI_RecordGetString(row,6);
4559 action = MSI_RecordGetInteger(row,7);
4561 deformat_string(package,section,&deformated_section);
4562 deformat_string(package,key,&deformated_key);
4563 deformat_string(package,value,&deformated_value);
4565 fullname = get_ini_file_name(package, row);
4567 if (action == 0)
4569 TRACE("Adding value %s to section %s in %s\n",
4570 debugstr_w(deformated_key), debugstr_w(deformated_section),
4571 debugstr_w(fullname));
4572 WritePrivateProfileStringW(deformated_section, deformated_key,
4573 deformated_value, fullname);
4575 else if (action == 1)
4577 WCHAR returned[10];
4578 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4579 returned, 10, fullname);
4580 if (returned[0] == 0)
4582 TRACE("Adding value %s to section %s in %s\n",
4583 debugstr_w(deformated_key), debugstr_w(deformated_section),
4584 debugstr_w(fullname));
4586 WritePrivateProfileStringW(deformated_section, deformated_key,
4587 deformated_value, fullname);
4590 else if (action == 3)
4591 FIXME("Append to existing section not yet implemented\n");
4593 uirow = MSI_CreateRecord(4);
4594 MSI_RecordSetStringW(uirow,1,identifier);
4595 MSI_RecordSetStringW(uirow,2,deformated_section);
4596 MSI_RecordSetStringW(uirow,3,deformated_key);
4597 MSI_RecordSetStringW(uirow,4,deformated_value);
4598 msi_ui_actiondata( package, szWriteIniValues, uirow );
4599 msiobj_release( &uirow->hdr );
4601 msi_free(fullname);
4602 msi_free(deformated_key);
4603 msi_free(deformated_value);
4604 msi_free(deformated_section);
4605 return ERROR_SUCCESS;
4608 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4610 static const WCHAR query[] = {
4611 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4612 '`','I','n','i','F','i','l','e','`',0};
4613 MSIQUERY *view;
4614 UINT rc;
4616 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4617 if (rc != ERROR_SUCCESS)
4618 return ERROR_SUCCESS;
4620 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4621 msiobj_release(&view->hdr);
4622 return rc;
4625 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4627 MSIPACKAGE *package = param;
4628 LPCWSTR component, section, key, value, identifier;
4629 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4630 MSICOMPONENT *comp;
4631 MSIRECORD *uirow;
4632 INT action;
4634 component = MSI_RecordGetString( row, 8 );
4635 comp = msi_get_loaded_component( package, component );
4636 if (!comp)
4637 return ERROR_SUCCESS;
4639 comp->Action = msi_get_component_action( package, comp );
4640 if (comp->Action != INSTALLSTATE_ABSENT)
4642 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4643 return ERROR_SUCCESS;
4646 identifier = MSI_RecordGetString( row, 1 );
4647 section = MSI_RecordGetString( row, 4 );
4648 key = MSI_RecordGetString( row, 5 );
4649 value = MSI_RecordGetString( row, 6 );
4650 action = MSI_RecordGetInteger( row, 7 );
4652 deformat_string( package, section, &deformated_section );
4653 deformat_string( package, key, &deformated_key );
4654 deformat_string( package, value, &deformated_value );
4656 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4658 filename = get_ini_file_name( package, row );
4660 TRACE("Removing key %s from section %s in %s\n",
4661 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4663 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4665 WARN("Unable to remove key %u\n", GetLastError());
4667 msi_free( filename );
4669 else
4670 FIXME("Unsupported action %d\n", action);
4673 uirow = MSI_CreateRecord( 4 );
4674 MSI_RecordSetStringW( uirow, 1, identifier );
4675 MSI_RecordSetStringW( uirow, 2, deformated_section );
4676 MSI_RecordSetStringW( uirow, 3, deformated_key );
4677 MSI_RecordSetStringW( uirow, 4, deformated_value );
4678 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4679 msiobj_release( &uirow->hdr );
4681 msi_free( deformated_key );
4682 msi_free( deformated_value );
4683 msi_free( deformated_section );
4684 return ERROR_SUCCESS;
4687 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4689 MSIPACKAGE *package = param;
4690 LPCWSTR component, section, key, value, identifier;
4691 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4692 MSICOMPONENT *comp;
4693 MSIRECORD *uirow;
4694 INT action;
4696 component = MSI_RecordGetString( row, 8 );
4697 comp = msi_get_loaded_component( package, component );
4698 if (!comp)
4699 return ERROR_SUCCESS;
4701 comp->Action = msi_get_component_action( package, comp );
4702 if (comp->Action != INSTALLSTATE_LOCAL)
4704 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4705 return ERROR_SUCCESS;
4708 identifier = MSI_RecordGetString( row, 1 );
4709 section = MSI_RecordGetString( row, 4 );
4710 key = MSI_RecordGetString( row, 5 );
4711 value = MSI_RecordGetString( row, 6 );
4712 action = MSI_RecordGetInteger( row, 7 );
4714 deformat_string( package, section, &deformated_section );
4715 deformat_string( package, key, &deformated_key );
4716 deformat_string( package, value, &deformated_value );
4718 if (action == msidbIniFileActionRemoveLine)
4720 filename = get_ini_file_name( package, row );
4722 TRACE("Removing key %s from section %s in %s\n",
4723 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4725 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4727 WARN("Unable to remove key %u\n", GetLastError());
4729 msi_free( filename );
4731 else
4732 FIXME("Unsupported action %d\n", action);
4734 uirow = MSI_CreateRecord( 4 );
4735 MSI_RecordSetStringW( uirow, 1, identifier );
4736 MSI_RecordSetStringW( uirow, 2, deformated_section );
4737 MSI_RecordSetStringW( uirow, 3, deformated_key );
4738 MSI_RecordSetStringW( uirow, 4, deformated_value );
4739 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4740 msiobj_release( &uirow->hdr );
4742 msi_free( deformated_key );
4743 msi_free( deformated_value );
4744 msi_free( deformated_section );
4745 return ERROR_SUCCESS;
4748 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4750 static const WCHAR query[] = {
4751 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4752 '`','I','n','i','F','i','l','e','`',0};
4753 static const WCHAR remove_query[] = {
4754 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4755 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4756 MSIQUERY *view;
4757 UINT rc;
4759 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4760 if (rc == ERROR_SUCCESS)
4762 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4763 msiobj_release( &view->hdr );
4764 if (rc != ERROR_SUCCESS)
4765 return rc;
4767 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4768 if (rc == ERROR_SUCCESS)
4770 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4771 msiobj_release( &view->hdr );
4772 if (rc != ERROR_SUCCESS)
4773 return rc;
4775 return ERROR_SUCCESS;
4778 static void register_dll( const WCHAR *dll, BOOL unregister )
4780 static const WCHAR regW[] =
4781 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4782 static const WCHAR unregW[] =
4783 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4784 PROCESS_INFORMATION pi;
4785 STARTUPINFOW si;
4786 WCHAR *cmd;
4788 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4790 if (unregister) sprintfW( cmd, unregW, dll );
4791 else sprintfW( cmd, regW, dll );
4793 memset( &si, 0, sizeof(STARTUPINFOW) );
4794 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4796 CloseHandle( pi.hThread );
4797 msi_dialog_check_messages( pi.hProcess );
4798 CloseHandle( pi.hProcess );
4800 msi_free( cmd );
4803 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4805 MSIPACKAGE *package = param;
4806 LPCWSTR filename;
4807 MSIFILE *file;
4808 MSIRECORD *uirow;
4810 filename = MSI_RecordGetString( row, 1 );
4811 file = msi_get_loaded_file( package, filename );
4812 if (!file)
4814 WARN("unable to find file %s\n", debugstr_w(filename));
4815 return ERROR_SUCCESS;
4817 file->Component->Action = msi_get_component_action( package, file->Component );
4818 if (file->Component->Action != INSTALLSTATE_LOCAL)
4820 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4821 return ERROR_SUCCESS;
4824 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4825 register_dll( file->TargetPath, FALSE );
4827 uirow = MSI_CreateRecord( 2 );
4828 MSI_RecordSetStringW( uirow, 1, file->File );
4829 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4830 msi_ui_actiondata( package, szSelfRegModules, uirow );
4831 msiobj_release( &uirow->hdr );
4833 return ERROR_SUCCESS;
4836 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4838 static const WCHAR query[] = {
4839 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4840 '`','S','e','l','f','R','e','g','`',0};
4841 MSIQUERY *view;
4842 UINT rc;
4844 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4845 if (rc != ERROR_SUCCESS)
4846 return ERROR_SUCCESS;
4848 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4849 msiobj_release(&view->hdr);
4850 return rc;
4853 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4855 MSIPACKAGE *package = param;
4856 LPCWSTR filename;
4857 MSIFILE *file;
4858 MSIRECORD *uirow;
4860 filename = MSI_RecordGetString( row, 1 );
4861 file = msi_get_loaded_file( package, filename );
4862 if (!file)
4864 WARN("unable to find file %s\n", debugstr_w(filename));
4865 return ERROR_SUCCESS;
4867 file->Component->Action = msi_get_component_action( package, file->Component );
4868 if (file->Component->Action != INSTALLSTATE_ABSENT)
4870 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4871 return ERROR_SUCCESS;
4874 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4875 register_dll( file->TargetPath, TRUE );
4877 uirow = MSI_CreateRecord( 2 );
4878 MSI_RecordSetStringW( uirow, 1, file->File );
4879 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4880 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4881 msiobj_release( &uirow->hdr );
4883 return ERROR_SUCCESS;
4886 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4888 static const WCHAR query[] = {
4889 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4890 '`','S','e','l','f','R','e','g','`',0};
4891 MSIQUERY *view;
4892 UINT rc;
4894 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4895 if (rc != ERROR_SUCCESS)
4896 return ERROR_SUCCESS;
4898 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4899 msiobj_release( &view->hdr );
4900 return rc;
4903 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4905 MSIFEATURE *feature;
4906 UINT rc;
4907 HKEY hkey = NULL, userdata = NULL;
4909 if (!msi_check_publish(package))
4910 return ERROR_SUCCESS;
4912 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4913 &hkey, TRUE);
4914 if (rc != ERROR_SUCCESS)
4915 goto end;
4917 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4918 &userdata, TRUE);
4919 if (rc != ERROR_SUCCESS)
4920 goto end;
4922 /* here the guids are base 85 encoded */
4923 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4925 ComponentList *cl;
4926 LPWSTR data = NULL;
4927 GUID clsid;
4928 INT size;
4929 BOOL absent = FALSE;
4930 MSIRECORD *uirow;
4932 if (feature->Level <= 0) continue;
4934 if (feature->Action != INSTALLSTATE_LOCAL &&
4935 feature->Action != INSTALLSTATE_SOURCE &&
4936 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4938 size = 1;
4939 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4941 size += 21;
4943 if (feature->Feature_Parent)
4944 size += strlenW( feature->Feature_Parent )+2;
4946 data = msi_alloc(size * sizeof(WCHAR));
4948 data[0] = 0;
4949 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4951 MSICOMPONENT* component = cl->component;
4952 WCHAR buf[21];
4954 buf[0] = 0;
4955 if (component->ComponentId)
4957 TRACE("From %s\n",debugstr_w(component->ComponentId));
4958 CLSIDFromString(component->ComponentId, &clsid);
4959 encode_base85_guid(&clsid,buf);
4960 TRACE("to %s\n",debugstr_w(buf));
4961 strcatW(data,buf);
4965 if (feature->Feature_Parent)
4967 static const WCHAR sep[] = {'\2',0};
4968 strcatW(data,sep);
4969 strcatW(data,feature->Feature_Parent);
4972 msi_reg_set_val_str( userdata, feature->Feature, data );
4973 msi_free(data);
4975 size = 0;
4976 if (feature->Feature_Parent)
4977 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4978 if (!absent)
4980 size += sizeof(WCHAR);
4981 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4982 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4984 else
4986 size += 2*sizeof(WCHAR);
4987 data = msi_alloc(size);
4988 data[0] = 0x6;
4989 data[1] = 0;
4990 if (feature->Feature_Parent)
4991 strcpyW( &data[1], feature->Feature_Parent );
4992 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4993 (LPBYTE)data,size);
4994 msi_free(data);
4997 /* the UI chunk */
4998 uirow = MSI_CreateRecord( 1 );
4999 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5000 msi_ui_actiondata( package, szPublishFeatures, uirow );
5001 msiobj_release( &uirow->hdr );
5002 /* FIXME: call msi_ui_progress? */
5005 end:
5006 RegCloseKey(hkey);
5007 RegCloseKey(userdata);
5008 return rc;
5011 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5013 UINT r;
5014 HKEY hkey;
5015 MSIRECORD *uirow;
5017 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5019 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5020 &hkey, FALSE);
5021 if (r == ERROR_SUCCESS)
5023 RegDeleteValueW(hkey, feature->Feature);
5024 RegCloseKey(hkey);
5027 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5028 &hkey, FALSE);
5029 if (r == ERROR_SUCCESS)
5031 RegDeleteValueW(hkey, feature->Feature);
5032 RegCloseKey(hkey);
5035 uirow = MSI_CreateRecord( 1 );
5036 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5037 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5038 msiobj_release( &uirow->hdr );
5040 return ERROR_SUCCESS;
5043 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5045 MSIFEATURE *feature;
5047 if (!msi_check_unpublish(package))
5048 return ERROR_SUCCESS;
5050 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5052 msi_unpublish_feature(package, feature);
5055 return ERROR_SUCCESS;
5058 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5060 SYSTEMTIME systime;
5061 DWORD size, langid;
5062 WCHAR date[9], *val, *buffer;
5063 const WCHAR *prop, *key;
5065 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5066 static const WCHAR modpath_fmt[] =
5067 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5068 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5069 static const WCHAR szModifyPath[] =
5070 {'M','o','d','i','f','y','P','a','t','h',0};
5071 static const WCHAR szUninstallString[] =
5072 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5073 static const WCHAR szEstimatedSize[] =
5074 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5075 static const WCHAR szDisplayVersion[] =
5076 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5077 static const WCHAR szInstallSource[] =
5078 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5079 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5080 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5081 static const WCHAR szAuthorizedCDFPrefix[] =
5082 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5083 static const WCHAR szARPCONTACT[] =
5084 {'A','R','P','C','O','N','T','A','C','T',0};
5085 static const WCHAR szContact[] =
5086 {'C','o','n','t','a','c','t',0};
5087 static const WCHAR szARPCOMMENTS[] =
5088 {'A','R','P','C','O','M','M','E','N','T','S',0};
5089 static const WCHAR szComments[] =
5090 {'C','o','m','m','e','n','t','s',0};
5091 static const WCHAR szProductName[] =
5092 {'P','r','o','d','u','c','t','N','a','m','e',0};
5093 static const WCHAR szDisplayName[] =
5094 {'D','i','s','p','l','a','y','N','a','m','e',0};
5095 static const WCHAR szARPHELPLINK[] =
5096 {'A','R','P','H','E','L','P','L','I','N','K',0};
5097 static const WCHAR szHelpLink[] =
5098 {'H','e','l','p','L','i','n','k',0};
5099 static const WCHAR szARPHELPTELEPHONE[] =
5100 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5101 static const WCHAR szHelpTelephone[] =
5102 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5103 static const WCHAR szARPINSTALLLOCATION[] =
5104 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5105 static const WCHAR szManufacturer[] =
5106 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5107 static const WCHAR szPublisher[] =
5108 {'P','u','b','l','i','s','h','e','r',0};
5109 static const WCHAR szARPREADME[] =
5110 {'A','R','P','R','E','A','D','M','E',0};
5111 static const WCHAR szReadme[] =
5112 {'R','e','a','d','M','e',0};
5113 static const WCHAR szARPSIZE[] =
5114 {'A','R','P','S','I','Z','E',0};
5115 static const WCHAR szSize[] =
5116 {'S','i','z','e',0};
5117 static const WCHAR szARPURLINFOABOUT[] =
5118 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5119 static const WCHAR szURLInfoAbout[] =
5120 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5121 static const WCHAR szARPURLUPDATEINFO[] =
5122 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5123 static const WCHAR szURLUpdateInfo[] =
5124 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5125 static const WCHAR szARPSYSTEMCOMPONENT[] =
5126 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5127 static const WCHAR szSystemComponent[] =
5128 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5130 static const WCHAR *propval[] = {
5131 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5132 szARPCONTACT, szContact,
5133 szARPCOMMENTS, szComments,
5134 szProductName, szDisplayName,
5135 szARPHELPLINK, szHelpLink,
5136 szARPHELPTELEPHONE, szHelpTelephone,
5137 szARPINSTALLLOCATION, szInstallLocation,
5138 szSourceDir, szInstallSource,
5139 szManufacturer, szPublisher,
5140 szARPREADME, szReadme,
5141 szARPSIZE, szSize,
5142 szARPURLINFOABOUT, szURLInfoAbout,
5143 szARPURLUPDATEINFO, szURLUpdateInfo,
5144 NULL
5146 const WCHAR **p = propval;
5148 while (*p)
5150 prop = *p++;
5151 key = *p++;
5152 val = msi_dup_property(package->db, prop);
5153 msi_reg_set_val_str(hkey, key, val);
5154 msi_free(val);
5157 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5158 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5160 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5162 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5163 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5164 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5165 msi_free(buffer);
5167 /* FIXME: Write real Estimated Size when we have it */
5168 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5170 GetLocalTime(&systime);
5171 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5172 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5174 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5175 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5177 buffer = msi_dup_property(package->db, szProductVersion);
5178 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5179 if (buffer)
5181 DWORD verdword = msi_version_str_to_dword(buffer);
5183 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5184 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5185 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5186 msi_free(buffer);
5189 return ERROR_SUCCESS;
5192 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5194 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5195 MSIRECORD *uirow;
5196 HKEY hkey, props, upgrade_key;
5197 UINT rc;
5199 /* FIXME: also need to publish if the product is in advertise mode */
5200 if (!msi_check_publish(package))
5201 return ERROR_SUCCESS;
5203 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5204 if (rc != ERROR_SUCCESS)
5205 return rc;
5207 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5208 if (rc != ERROR_SUCCESS)
5209 goto done;
5211 rc = msi_publish_install_properties(package, hkey);
5212 if (rc != ERROR_SUCCESS)
5213 goto done;
5215 rc = msi_publish_install_properties(package, props);
5216 if (rc != ERROR_SUCCESS)
5217 goto done;
5219 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5220 if (upgrade_code)
5222 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5223 if (rc == ERROR_SUCCESS)
5225 squash_guid( package->ProductCode, squashed_pc );
5226 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5227 RegCloseKey( upgrade_key );
5229 msi_free( upgrade_code );
5231 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5232 package->delete_on_close = FALSE;
5234 done:
5235 uirow = MSI_CreateRecord( 1 );
5236 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5237 msi_ui_actiondata( package, szRegisterProduct, uirow );
5238 msiobj_release( &uirow->hdr );
5240 RegCloseKey(hkey);
5241 return ERROR_SUCCESS;
5244 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5246 return execute_script(package, SCRIPT_INSTALL);
5249 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5251 MSIPACKAGE *package = param;
5252 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5253 WCHAR *p, *icon_path;
5255 if (!icon) return ERROR_SUCCESS;
5256 if ((icon_path = msi_build_icon_path( package, icon )))
5258 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5259 DeleteFileW( icon_path );
5260 if ((p = strrchrW( icon_path, '\\' )))
5262 *p = 0;
5263 RemoveDirectoryW( icon_path );
5265 msi_free( icon_path );
5267 return ERROR_SUCCESS;
5270 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5272 static const WCHAR query[]= {
5273 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5274 MSIQUERY *view;
5275 UINT r;
5277 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5278 if (r == ERROR_SUCCESS)
5280 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5281 msiobj_release( &view->hdr );
5282 if (r != ERROR_SUCCESS)
5283 return r;
5285 return ERROR_SUCCESS;
5288 static void remove_product_upgrade_code( MSIPACKAGE *package )
5290 WCHAR *code, product[SQUASHED_GUID_SIZE];
5291 HKEY hkey;
5292 LONG res;
5293 DWORD count;
5295 squash_guid( package->ProductCode, product );
5296 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5298 WARN( "upgrade code not found\n" );
5299 return;
5301 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5303 RegDeleteValueW( hkey, product );
5304 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5305 RegCloseKey( hkey );
5306 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5308 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5310 RegDeleteValueW( hkey, product );
5311 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5312 RegCloseKey( hkey );
5313 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5315 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5317 RegDeleteValueW( hkey, product );
5318 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5319 RegCloseKey( hkey );
5320 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5323 msi_free( code );
5326 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5328 MSIPATCHINFO *patch;
5330 MSIREG_DeleteProductKey(package->ProductCode);
5331 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5332 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5334 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5335 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5336 MSIREG_DeleteUserProductKey(package->ProductCode);
5337 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5339 remove_product_upgrade_code( package );
5341 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5343 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5344 if (!strcmpW( package->ProductCode, patch->products ))
5346 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5347 patch->delete_on_close = TRUE;
5349 /* FIXME: remove local patch package if this is the last product */
5351 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5352 package->delete_on_close = TRUE;
5354 msi_unpublish_icons( package );
5355 return ERROR_SUCCESS;
5358 static BOOL is_full_uninstall( MSIPACKAGE *package )
5360 WCHAR **features, *remove = msi_dup_property( package->db, szRemove );
5361 MSIFEATURE *feature;
5362 BOOL ret = TRUE;
5363 UINT i;
5365 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5367 if (feature->Action == INSTALLSTATE_LOCAL) ret = FALSE;
5370 features = msi_split_string( remove, ',' );
5371 for (i = 0; features && features[i]; i++)
5373 if (!strcmpW( features[i], szAll )) ret = TRUE;
5376 msi_free(features);
5377 msi_free(remove);
5378 return ret;
5381 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5383 UINT rc;
5385 /* first do the same as an InstallExecute */
5386 rc = execute_script(package, SCRIPT_INSTALL);
5387 if (rc != ERROR_SUCCESS)
5388 return rc;
5390 /* then handle commit actions */
5391 rc = execute_script(package, SCRIPT_COMMIT);
5392 if (rc != ERROR_SUCCESS)
5393 return rc;
5395 if (is_full_uninstall(package))
5396 rc = ACTION_UnpublishProduct(package);
5398 return rc;
5401 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5403 static const WCHAR RunOnce[] = {
5404 'S','o','f','t','w','a','r','e','\\',
5405 'M','i','c','r','o','s','o','f','t','\\',
5406 'W','i','n','d','o','w','s','\\',
5407 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5408 'R','u','n','O','n','c','e',0};
5409 static const WCHAR InstallRunOnce[] = {
5410 'S','o','f','t','w','a','r','e','\\',
5411 'M','i','c','r','o','s','o','f','t','\\',
5412 'W','i','n','d','o','w','s','\\',
5413 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5414 'I','n','s','t','a','l','l','e','r','\\',
5415 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5417 static const WCHAR msiexec_fmt[] = {
5418 '%','s',
5419 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5420 '\"','%','s','\"',0};
5421 static const WCHAR install_fmt[] = {
5422 '/','I',' ','\"','%','s','\"',' ',
5423 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5424 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5425 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5426 HKEY hkey;
5428 squash_guid( package->ProductCode, squashed_pc );
5430 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5431 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5432 snprintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), msiexec_fmt, sysdir, squashed_pc );
5434 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5435 RegCloseKey(hkey);
5437 TRACE("Reboot command %s\n",debugstr_w(buffer));
5439 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5440 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5442 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5443 RegCloseKey(hkey);
5445 return ERROR_INSTALL_SUSPEND;
5448 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5450 static const WCHAR query[] =
5451 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5452 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5453 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5454 MSIRECORD *rec, *row;
5455 DWORD i, size = 0;
5456 va_list va;
5457 const WCHAR *str;
5458 WCHAR *data;
5460 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5462 rec = MSI_CreateRecord( count + 2 );
5463 str = MSI_RecordGetString( row, 1 );
5464 MSI_RecordSetStringW( rec, 0, str );
5465 msiobj_release( &row->hdr );
5466 MSI_RecordSetInteger( rec, 1, error );
5468 va_start( va, count );
5469 for (i = 0; i < count; i++)
5471 str = va_arg( va, const WCHAR *);
5472 MSI_RecordSetStringW( rec, i + 2, str );
5474 va_end( va );
5476 MSI_FormatRecordW( package, rec, NULL, &size );
5477 size++;
5478 data = msi_alloc( size * sizeof(WCHAR) );
5479 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5480 else data[0] = 0;
5481 msiobj_release( &rec->hdr );
5482 return data;
5485 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5487 DWORD attrib;
5488 UINT rc;
5491 * We are currently doing what should be done here in the top level Install
5492 * however for Administrative and uninstalls this step will be needed
5494 if (!package->PackagePath)
5495 return ERROR_SUCCESS;
5497 msi_set_sourcedir_props(package, TRUE);
5499 attrib = GetFileAttributesW(package->db->path);
5500 if (attrib == INVALID_FILE_ATTRIBUTES)
5502 LPWSTR prompt, msg;
5503 DWORD size = 0;
5505 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5506 package->Context, MSICODE_PRODUCT,
5507 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5508 if (rc == ERROR_MORE_DATA)
5510 prompt = msi_alloc(size * sizeof(WCHAR));
5511 MsiSourceListGetInfoW(package->ProductCode, NULL,
5512 package->Context, MSICODE_PRODUCT,
5513 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5515 else
5516 prompt = strdupW(package->db->path);
5518 msg = msi_build_error_string(package, 1302, 1, prompt);
5519 msi_free(prompt);
5520 while(attrib == INVALID_FILE_ATTRIBUTES)
5522 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5523 if (rc == IDCANCEL)
5525 msi_free(msg);
5526 return ERROR_INSTALL_USEREXIT;
5528 attrib = GetFileAttributesW(package->db->path);
5530 msi_free(msg);
5531 rc = ERROR_SUCCESS;
5533 else
5534 return ERROR_SUCCESS;
5536 return rc;
5539 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5541 HKEY hkey = 0;
5542 LPWSTR buffer, productid = NULL;
5543 UINT i, rc = ERROR_SUCCESS;
5544 MSIRECORD *uirow;
5546 static const WCHAR szPropKeys[][80] =
5548 {'P','r','o','d','u','c','t','I','D',0},
5549 {'U','S','E','R','N','A','M','E',0},
5550 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5551 {0},
5554 static const WCHAR szRegKeys[][80] =
5556 {'P','r','o','d','u','c','t','I','D',0},
5557 {'R','e','g','O','w','n','e','r',0},
5558 {'R','e','g','C','o','m','p','a','n','y',0},
5559 {0},
5562 if (msi_check_unpublish(package))
5564 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5565 goto end;
5568 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5569 if (!productid)
5570 goto end;
5572 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5573 NULL, &hkey, TRUE);
5574 if (rc != ERROR_SUCCESS)
5575 goto end;
5577 for( i = 0; szPropKeys[i][0]; i++ )
5579 buffer = msi_dup_property( package->db, szPropKeys[i] );
5580 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5581 msi_free( buffer );
5584 end:
5585 uirow = MSI_CreateRecord( 1 );
5586 MSI_RecordSetStringW( uirow, 1, productid );
5587 msi_ui_actiondata( package, szRegisterUser, uirow );
5588 msiobj_release( &uirow->hdr );
5590 msi_free(productid);
5591 RegCloseKey(hkey);
5592 return rc;
5596 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5598 UINT rc;
5600 package->script->InWhatSequence |= SEQUENCE_EXEC;
5601 rc = ACTION_ProcessExecSequence(package,FALSE);
5602 return rc;
5605 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5607 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5608 WCHAR productid_85[21], component_85[21], *ret;
5609 GUID clsid;
5610 DWORD sz;
5612 /* > is used if there is a component GUID and < if not. */
5614 productid_85[0] = 0;
5615 component_85[0] = 0;
5616 CLSIDFromString( package->ProductCode, &clsid );
5618 encode_base85_guid( &clsid, productid_85 );
5619 if (component)
5621 CLSIDFromString( component->ComponentId, &clsid );
5622 encode_base85_guid( &clsid, component_85 );
5625 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5626 debugstr_w(component_85));
5628 sz = 20 + strlenW( feature ) + 20 + 3;
5629 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5630 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5631 return ret;
5634 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5636 MSIPACKAGE *package = param;
5637 LPCWSTR compgroupid, component, feature, qualifier, text;
5638 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5639 HKEY hkey = NULL;
5640 UINT rc;
5641 MSICOMPONENT *comp;
5642 MSIFEATURE *feat;
5643 DWORD sz;
5644 MSIRECORD *uirow;
5645 int len;
5647 feature = MSI_RecordGetString(rec, 5);
5648 feat = msi_get_loaded_feature(package, feature);
5649 if (!feat)
5650 return ERROR_SUCCESS;
5652 feat->Action = msi_get_feature_action( package, feat );
5653 if (feat->Action != INSTALLSTATE_LOCAL &&
5654 feat->Action != INSTALLSTATE_SOURCE &&
5655 feat->Action != INSTALLSTATE_ADVERTISED)
5657 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5658 return ERROR_SUCCESS;
5661 component = MSI_RecordGetString(rec, 3);
5662 comp = msi_get_loaded_component(package, component);
5663 if (!comp)
5664 return ERROR_SUCCESS;
5666 compgroupid = MSI_RecordGetString(rec,1);
5667 qualifier = MSI_RecordGetString(rec,2);
5669 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5670 if (rc != ERROR_SUCCESS)
5671 goto end;
5673 advertise = msi_create_component_advertise_string( package, comp, feature );
5674 text = MSI_RecordGetString( rec, 4 );
5675 if (text)
5677 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5678 strcpyW( p, advertise );
5679 strcatW( p, text );
5680 msi_free( advertise );
5681 advertise = p;
5683 existing = msi_reg_get_val_str( hkey, qualifier );
5685 sz = strlenW( advertise ) + 1;
5686 if (existing)
5688 for (p = existing; *p; p += len)
5690 len = strlenW( p ) + 1;
5691 if (strcmpW( advertise, p )) sz += len;
5694 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5696 rc = ERROR_OUTOFMEMORY;
5697 goto end;
5699 q = output;
5700 if (existing)
5702 for (p = existing; *p; p += len)
5704 len = strlenW( p ) + 1;
5705 if (strcmpW( advertise, p ))
5707 memcpy( q, p, len * sizeof(WCHAR) );
5708 q += len;
5712 strcpyW( q, advertise );
5713 q[strlenW( q ) + 1] = 0;
5715 msi_reg_set_val_multi_str( hkey, qualifier, output );
5717 end:
5718 RegCloseKey(hkey);
5719 msi_free( output );
5720 msi_free( advertise );
5721 msi_free( existing );
5723 /* the UI chunk */
5724 uirow = MSI_CreateRecord( 2 );
5725 MSI_RecordSetStringW( uirow, 1, compgroupid );
5726 MSI_RecordSetStringW( uirow, 2, qualifier);
5727 msi_ui_actiondata( package, szPublishComponents, uirow );
5728 msiobj_release( &uirow->hdr );
5729 /* FIXME: call ui_progress? */
5731 return rc;
5735 * At present I am ignorning the advertised components part of this and only
5736 * focusing on the qualified component sets
5738 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5740 static const WCHAR query[] = {
5741 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5742 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5743 MSIQUERY *view;
5744 UINT rc;
5746 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5747 if (rc != ERROR_SUCCESS)
5748 return ERROR_SUCCESS;
5750 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5751 msiobj_release(&view->hdr);
5752 return rc;
5755 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5757 static const WCHAR szInstallerComponents[] = {
5758 'S','o','f','t','w','a','r','e','\\',
5759 'M','i','c','r','o','s','o','f','t','\\',
5760 'I','n','s','t','a','l','l','e','r','\\',
5761 'C','o','m','p','o','n','e','n','t','s','\\',0};
5763 MSIPACKAGE *package = param;
5764 LPCWSTR compgroupid, component, feature, qualifier;
5765 MSICOMPONENT *comp;
5766 MSIFEATURE *feat;
5767 MSIRECORD *uirow;
5768 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5769 LONG res;
5771 feature = MSI_RecordGetString( rec, 5 );
5772 feat = msi_get_loaded_feature( package, feature );
5773 if (!feat)
5774 return ERROR_SUCCESS;
5776 feat->Action = msi_get_feature_action( package, feat );
5777 if (feat->Action != INSTALLSTATE_ABSENT)
5779 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5780 return ERROR_SUCCESS;
5783 component = MSI_RecordGetString( rec, 3 );
5784 comp = msi_get_loaded_component( package, component );
5785 if (!comp)
5786 return ERROR_SUCCESS;
5788 compgroupid = MSI_RecordGetString( rec, 1 );
5789 qualifier = MSI_RecordGetString( rec, 2 );
5791 squash_guid( compgroupid, squashed );
5792 strcpyW( keypath, szInstallerComponents );
5793 strcatW( keypath, squashed );
5795 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5796 if (res != ERROR_SUCCESS)
5798 WARN("Unable to delete component key %d\n", res);
5801 uirow = MSI_CreateRecord( 2 );
5802 MSI_RecordSetStringW( uirow, 1, compgroupid );
5803 MSI_RecordSetStringW( uirow, 2, qualifier );
5804 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5805 msiobj_release( &uirow->hdr );
5807 return ERROR_SUCCESS;
5810 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5812 static const WCHAR query[] = {
5813 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5814 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5815 MSIQUERY *view;
5816 UINT rc;
5818 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5819 if (rc != ERROR_SUCCESS)
5820 return ERROR_SUCCESS;
5822 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5823 msiobj_release( &view->hdr );
5824 return rc;
5827 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5829 static const WCHAR query[] =
5830 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5831 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5832 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5833 MSIPACKAGE *package = param;
5834 MSICOMPONENT *component;
5835 MSIRECORD *row;
5836 MSIFILE *file;
5837 SC_HANDLE hscm = NULL, service = NULL;
5838 LPCWSTR comp, key;
5839 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5840 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5841 DWORD serv_type, start_type, err_control;
5842 BOOL is_vital;
5843 SERVICE_DESCRIPTIONW sd = {NULL};
5844 UINT ret = ERROR_SUCCESS;
5846 comp = MSI_RecordGetString( rec, 12 );
5847 component = msi_get_loaded_component( package, comp );
5848 if (!component)
5850 WARN("service component not found\n");
5851 goto done;
5853 component->Action = msi_get_component_action( package, component );
5854 if (component->Action != INSTALLSTATE_LOCAL)
5856 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5857 goto done;
5859 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5860 if (!hscm)
5862 ERR("Failed to open the SC Manager!\n");
5863 goto done;
5866 start_type = MSI_RecordGetInteger(rec, 5);
5867 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5868 goto done;
5870 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5871 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5872 serv_type = MSI_RecordGetInteger(rec, 4);
5873 err_control = MSI_RecordGetInteger(rec, 6);
5874 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5875 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5876 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5877 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5878 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5879 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5881 /* Should the complete install fail if CreateService fails? */
5882 is_vital = (err_control & msidbServiceInstallErrorControlVital);
5884 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
5885 CreateService (under Windows) would fail if not. */
5886 err_control &= ~msidbServiceInstallErrorControlVital;
5888 /* fetch the service path */
5889 row = MSI_QueryGetRecord(package->db, query, comp);
5890 if (!row)
5892 ERR("Query failed\n");
5893 goto done;
5895 if (!(key = MSI_RecordGetString(row, 6)))
5897 msiobj_release(&row->hdr);
5898 goto done;
5900 file = msi_get_loaded_file(package, key);
5901 msiobj_release(&row->hdr);
5902 if (!file)
5904 ERR("Failed to load the service file\n");
5905 goto done;
5908 if (!args || !args[0]) image_path = file->TargetPath;
5909 else
5911 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5912 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5914 ret = ERROR_OUTOFMEMORY;
5915 goto done;
5918 strcpyW(image_path, file->TargetPath);
5919 strcatW(image_path, szSpace);
5920 strcatW(image_path, args);
5922 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5923 start_type, err_control, image_path, load_order,
5924 NULL, depends, serv_name, pass);
5926 if (!service)
5928 if (GetLastError() != ERROR_SERVICE_EXISTS)
5930 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5931 if (is_vital)
5932 ret = ERROR_INSTALL_FAILURE;
5936 else if (sd.lpDescription)
5938 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5939 WARN("failed to set service description %u\n", GetLastError());
5942 if (image_path != file->TargetPath) msi_free(image_path);
5943 done:
5944 if (service) CloseServiceHandle(service);
5945 if (hscm) CloseServiceHandle(hscm);
5946 msi_free(name);
5947 msi_free(disp);
5948 msi_free(sd.lpDescription);
5949 msi_free(load_order);
5950 msi_free(serv_name);
5951 msi_free(pass);
5952 msi_free(depends);
5953 msi_free(args);
5955 return ret;
5958 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5960 static const WCHAR query[] = {
5961 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5962 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5963 MSIQUERY *view;
5964 UINT rc;
5966 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5967 if (rc != ERROR_SUCCESS)
5968 return ERROR_SUCCESS;
5970 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5971 msiobj_release(&view->hdr);
5972 return rc;
5975 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5976 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5978 LPCWSTR *vector, *temp_vector;
5979 LPWSTR p, q;
5980 DWORD sep_len;
5982 static const WCHAR separator[] = {'[','~',']',0};
5984 *numargs = 0;
5985 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5987 if (!args)
5988 return NULL;
5990 vector = msi_alloc(sizeof(LPWSTR));
5991 if (!vector)
5992 return NULL;
5994 p = args;
5997 (*numargs)++;
5998 vector[*numargs - 1] = p;
6000 if ((q = strstrW(p, separator)))
6002 *q = '\0';
6004 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6005 if (!temp_vector)
6007 msi_free(vector);
6008 return NULL;
6010 vector = temp_vector;
6012 p = q + sep_len;
6014 } while (q);
6016 return vector;
6019 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6021 MSIPACKAGE *package = param;
6022 MSICOMPONENT *comp;
6023 MSIRECORD *uirow;
6024 SC_HANDLE scm = NULL, service = NULL;
6025 LPCWSTR component, *vector = NULL;
6026 LPWSTR name, args, display_name = NULL;
6027 DWORD event, numargs, len, wait, dummy;
6028 UINT r = ERROR_FUNCTION_FAILED;
6029 SERVICE_STATUS_PROCESS status;
6030 ULONGLONG start_time;
6032 component = MSI_RecordGetString(rec, 6);
6033 comp = msi_get_loaded_component(package, component);
6034 if (!comp)
6035 return ERROR_SUCCESS;
6037 event = MSI_RecordGetInteger( rec, 3 );
6038 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6040 comp->Action = msi_get_component_action( package, comp );
6041 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6042 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6044 TRACE("not starting %s\n", debugstr_w(name));
6045 msi_free( name );
6046 return ERROR_SUCCESS;
6049 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6050 wait = MSI_RecordGetInteger(rec, 5);
6052 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6053 if (!scm)
6055 ERR("Failed to open the service control manager\n");
6056 goto done;
6059 len = 0;
6060 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6061 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6063 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6064 GetServiceDisplayNameW( scm, name, display_name, &len );
6067 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6068 if (!service)
6070 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6071 goto done;
6074 vector = msi_service_args_to_vector(args, &numargs);
6076 if (!StartServiceW(service, numargs, vector) &&
6077 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6079 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6080 goto done;
6083 r = ERROR_SUCCESS;
6084 if (wait)
6086 /* wait for at most 30 seconds for the service to be up and running */
6087 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6088 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6090 TRACE("failed to query service status (%u)\n", GetLastError());
6091 goto done;
6093 start_time = GetTickCount64();
6094 while (status.dwCurrentState == SERVICE_START_PENDING)
6096 if (GetTickCount64() - start_time > 30000) break;
6097 Sleep(1000);
6098 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6099 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6101 TRACE("failed to query service status (%u)\n", GetLastError());
6102 goto done;
6105 if (status.dwCurrentState != SERVICE_RUNNING)
6107 WARN("service failed to start %u\n", status.dwCurrentState);
6108 r = ERROR_FUNCTION_FAILED;
6112 done:
6113 uirow = MSI_CreateRecord( 2 );
6114 MSI_RecordSetStringW( uirow, 1, display_name );
6115 MSI_RecordSetStringW( uirow, 2, name );
6116 msi_ui_actiondata( package, szStartServices, uirow );
6117 msiobj_release( &uirow->hdr );
6119 if (service) CloseServiceHandle(service);
6120 if (scm) CloseServiceHandle(scm);
6122 msi_free(name);
6123 msi_free(args);
6124 msi_free(vector);
6125 msi_free(display_name);
6126 return r;
6129 static UINT ACTION_StartServices( MSIPACKAGE *package )
6131 static const WCHAR query[] = {
6132 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6133 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6134 MSIQUERY *view;
6135 UINT rc;
6137 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6138 if (rc != ERROR_SUCCESS)
6139 return ERROR_SUCCESS;
6141 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6142 msiobj_release(&view->hdr);
6143 return rc;
6146 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6148 DWORD i, needed, count;
6149 ENUM_SERVICE_STATUSW *dependencies;
6150 SERVICE_STATUS ss;
6151 SC_HANDLE depserv;
6152 BOOL stopped, ret = FALSE;
6154 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6155 0, &needed, &count))
6156 return TRUE;
6158 if (GetLastError() != ERROR_MORE_DATA)
6159 return FALSE;
6161 dependencies = msi_alloc(needed);
6162 if (!dependencies)
6163 return FALSE;
6165 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6166 needed, &needed, &count))
6167 goto done;
6169 for (i = 0; i < count; i++)
6171 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6172 SERVICE_STOP | SERVICE_QUERY_STATUS);
6173 if (!depserv)
6174 goto done;
6176 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6177 CloseServiceHandle(depserv);
6178 if (!stopped)
6179 goto done;
6182 ret = TRUE;
6184 done:
6185 msi_free(dependencies);
6186 return ret;
6189 static UINT stop_service( LPCWSTR name )
6191 SC_HANDLE scm = NULL, service = NULL;
6192 SERVICE_STATUS status;
6193 SERVICE_STATUS_PROCESS ssp;
6194 DWORD needed;
6196 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6197 if (!scm)
6199 WARN("Failed to open the SCM: %d\n", GetLastError());
6200 goto done;
6203 service = OpenServiceW(scm, name,
6204 SERVICE_STOP |
6205 SERVICE_QUERY_STATUS |
6206 SERVICE_ENUMERATE_DEPENDENTS);
6207 if (!service)
6209 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6210 goto done;
6213 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6214 sizeof(SERVICE_STATUS_PROCESS), &needed))
6216 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6217 goto done;
6220 if (ssp.dwCurrentState == SERVICE_STOPPED)
6221 goto done;
6223 stop_service_dependents(scm, service);
6225 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6226 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6228 done:
6229 if (service) CloseServiceHandle(service);
6230 if (scm) CloseServiceHandle(scm);
6232 return ERROR_SUCCESS;
6235 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6237 MSIPACKAGE *package = param;
6238 MSICOMPONENT *comp;
6239 MSIRECORD *uirow;
6240 LPCWSTR component;
6241 WCHAR *name, *display_name = NULL;
6242 DWORD event, len;
6243 SC_HANDLE scm;
6245 component = MSI_RecordGetString( rec, 6 );
6246 comp = msi_get_loaded_component( package, component );
6247 if (!comp)
6248 return ERROR_SUCCESS;
6250 event = MSI_RecordGetInteger( rec, 3 );
6251 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6253 comp->Action = msi_get_component_action( package, comp );
6254 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6255 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6257 TRACE("not stopping %s\n", debugstr_w(name));
6258 msi_free( name );
6259 return ERROR_SUCCESS;
6262 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6263 if (!scm)
6265 ERR("Failed to open the service control manager\n");
6266 goto done;
6269 len = 0;
6270 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6271 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6273 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6274 GetServiceDisplayNameW( scm, name, display_name, &len );
6276 CloseServiceHandle( scm );
6278 stop_service( name );
6280 done:
6281 uirow = MSI_CreateRecord( 2 );
6282 MSI_RecordSetStringW( uirow, 1, display_name );
6283 MSI_RecordSetStringW( uirow, 2, name );
6284 msi_ui_actiondata( package, szStopServices, uirow );
6285 msiobj_release( &uirow->hdr );
6287 msi_free( name );
6288 msi_free( display_name );
6289 return ERROR_SUCCESS;
6292 static UINT ACTION_StopServices( MSIPACKAGE *package )
6294 static const WCHAR query[] = {
6295 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6296 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6297 MSIQUERY *view;
6298 UINT rc;
6300 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6301 if (rc != ERROR_SUCCESS)
6302 return ERROR_SUCCESS;
6304 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6305 msiobj_release(&view->hdr);
6306 return rc;
6309 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6311 MSIPACKAGE *package = param;
6312 MSICOMPONENT *comp;
6313 MSIRECORD *uirow;
6314 LPWSTR name = NULL, display_name = NULL;
6315 DWORD event, len;
6316 SC_HANDLE scm = NULL, service = NULL;
6318 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6319 if (!comp)
6320 return ERROR_SUCCESS;
6322 event = MSI_RecordGetInteger( rec, 3 );
6323 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6325 comp->Action = msi_get_component_action( package, comp );
6326 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6327 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6329 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6330 msi_free( name );
6331 return ERROR_SUCCESS;
6333 stop_service( name );
6335 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6336 if (!scm)
6338 WARN("Failed to open the SCM: %d\n", GetLastError());
6339 goto done;
6342 len = 0;
6343 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6344 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6346 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6347 GetServiceDisplayNameW( scm, name, display_name, &len );
6350 service = OpenServiceW( scm, name, DELETE );
6351 if (!service)
6353 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6354 goto done;
6357 if (!DeleteService( service ))
6358 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6360 done:
6361 uirow = MSI_CreateRecord( 2 );
6362 MSI_RecordSetStringW( uirow, 1, display_name );
6363 MSI_RecordSetStringW( uirow, 2, name );
6364 msi_ui_actiondata( package, szDeleteServices, uirow );
6365 msiobj_release( &uirow->hdr );
6367 if (service) CloseServiceHandle( service );
6368 if (scm) CloseServiceHandle( scm );
6369 msi_free( name );
6370 msi_free( display_name );
6372 return ERROR_SUCCESS;
6375 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6377 static const WCHAR query[] = {
6378 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6379 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6380 MSIQUERY *view;
6381 UINT rc;
6383 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6384 if (rc != ERROR_SUCCESS)
6385 return ERROR_SUCCESS;
6387 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6388 msiobj_release( &view->hdr );
6389 return rc;
6392 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6394 MSIPACKAGE *package = param;
6395 LPWSTR driver, driver_path, ptr;
6396 WCHAR outpath[MAX_PATH];
6397 MSIFILE *driver_file = NULL, *setup_file = NULL;
6398 MSICOMPONENT *comp;
6399 MSIRECORD *uirow;
6400 LPCWSTR desc, file_key, component;
6401 DWORD len, usage;
6402 UINT r = ERROR_SUCCESS;
6404 static const WCHAR driver_fmt[] = {
6405 'D','r','i','v','e','r','=','%','s',0};
6406 static const WCHAR setup_fmt[] = {
6407 'S','e','t','u','p','=','%','s',0};
6408 static const WCHAR usage_fmt[] = {
6409 'F','i','l','e','U','s','a','g','e','=','1',0};
6411 component = MSI_RecordGetString( rec, 2 );
6412 comp = msi_get_loaded_component( package, component );
6413 if (!comp)
6414 return ERROR_SUCCESS;
6416 comp->Action = msi_get_component_action( package, comp );
6417 if (comp->Action != INSTALLSTATE_LOCAL)
6419 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6420 return ERROR_SUCCESS;
6422 desc = MSI_RecordGetString(rec, 3);
6424 file_key = MSI_RecordGetString( rec, 4 );
6425 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6427 file_key = MSI_RecordGetString( rec, 5 );
6428 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6430 if (!driver_file)
6432 ERR("ODBC Driver entry not found!\n");
6433 return ERROR_FUNCTION_FAILED;
6436 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6437 if (setup_file)
6438 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6439 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6441 driver = msi_alloc(len * sizeof(WCHAR));
6442 if (!driver)
6443 return ERROR_OUTOFMEMORY;
6445 ptr = driver;
6446 lstrcpyW(ptr, desc);
6447 ptr += lstrlenW(ptr) + 1;
6449 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6450 ptr += len + 1;
6452 if (setup_file)
6454 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6455 ptr += len + 1;
6458 lstrcpyW(ptr, usage_fmt);
6459 ptr += lstrlenW(ptr) + 1;
6460 *ptr = '\0';
6462 if (!driver_file->TargetPath)
6464 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6465 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6467 driver_path = strdupW(driver_file->TargetPath);
6468 ptr = strrchrW(driver_path, '\\');
6469 if (ptr) *ptr = '\0';
6471 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6472 NULL, ODBC_INSTALL_COMPLETE, &usage))
6474 ERR("Failed to install SQL driver!\n");
6475 r = ERROR_FUNCTION_FAILED;
6478 uirow = MSI_CreateRecord( 5 );
6479 MSI_RecordSetStringW( uirow, 1, desc );
6480 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6481 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6482 msi_ui_actiondata( package, szInstallODBC, uirow );
6483 msiobj_release( &uirow->hdr );
6485 msi_free(driver);
6486 msi_free(driver_path);
6488 return r;
6491 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6493 MSIPACKAGE *package = param;
6494 LPWSTR translator, translator_path, ptr;
6495 WCHAR outpath[MAX_PATH];
6496 MSIFILE *translator_file = NULL, *setup_file = NULL;
6497 MSICOMPONENT *comp;
6498 MSIRECORD *uirow;
6499 LPCWSTR desc, file_key, component;
6500 DWORD len, usage;
6501 UINT r = ERROR_SUCCESS;
6503 static const WCHAR translator_fmt[] = {
6504 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6505 static const WCHAR setup_fmt[] = {
6506 'S','e','t','u','p','=','%','s',0};
6508 component = MSI_RecordGetString( rec, 2 );
6509 comp = msi_get_loaded_component( package, component );
6510 if (!comp)
6511 return ERROR_SUCCESS;
6513 comp->Action = msi_get_component_action( package, comp );
6514 if (comp->Action != INSTALLSTATE_LOCAL)
6516 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6517 return ERROR_SUCCESS;
6519 desc = MSI_RecordGetString(rec, 3);
6521 file_key = MSI_RecordGetString( rec, 4 );
6522 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6524 file_key = MSI_RecordGetString( rec, 5 );
6525 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6527 if (!translator_file)
6529 ERR("ODBC Translator entry not found!\n");
6530 return ERROR_FUNCTION_FAILED;
6533 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6534 if (setup_file)
6535 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6537 translator = msi_alloc(len * sizeof(WCHAR));
6538 if (!translator)
6539 return ERROR_OUTOFMEMORY;
6541 ptr = translator;
6542 lstrcpyW(ptr, desc);
6543 ptr += lstrlenW(ptr) + 1;
6545 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6546 ptr += len + 1;
6548 if (setup_file)
6550 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6551 ptr += len + 1;
6553 *ptr = '\0';
6555 translator_path = strdupW(translator_file->TargetPath);
6556 ptr = strrchrW(translator_path, '\\');
6557 if (ptr) *ptr = '\0';
6559 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6560 NULL, ODBC_INSTALL_COMPLETE, &usage))
6562 ERR("Failed to install SQL translator!\n");
6563 r = ERROR_FUNCTION_FAILED;
6566 uirow = MSI_CreateRecord( 5 );
6567 MSI_RecordSetStringW( uirow, 1, desc );
6568 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6569 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6570 msi_ui_actiondata( package, szInstallODBC, uirow );
6571 msiobj_release( &uirow->hdr );
6573 msi_free(translator);
6574 msi_free(translator_path);
6576 return r;
6579 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6581 MSIPACKAGE *package = param;
6582 MSICOMPONENT *comp;
6583 LPWSTR attrs;
6584 LPCWSTR desc, driver, component;
6585 WORD request = ODBC_ADD_SYS_DSN;
6586 INT registration;
6587 DWORD len;
6588 UINT r = ERROR_SUCCESS;
6589 MSIRECORD *uirow;
6591 static const WCHAR attrs_fmt[] = {
6592 'D','S','N','=','%','s',0 };
6594 component = MSI_RecordGetString( rec, 2 );
6595 comp = msi_get_loaded_component( package, component );
6596 if (!comp)
6597 return ERROR_SUCCESS;
6599 comp->Action = msi_get_component_action( package, comp );
6600 if (comp->Action != INSTALLSTATE_LOCAL)
6602 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6603 return ERROR_SUCCESS;
6606 desc = MSI_RecordGetString(rec, 3);
6607 driver = MSI_RecordGetString(rec, 4);
6608 registration = MSI_RecordGetInteger(rec, 5);
6610 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6611 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6613 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6614 attrs = msi_alloc(len * sizeof(WCHAR));
6615 if (!attrs)
6616 return ERROR_OUTOFMEMORY;
6618 len = sprintfW(attrs, attrs_fmt, desc);
6619 attrs[len + 1] = 0;
6621 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6623 ERR("Failed to install SQL data source!\n");
6624 r = ERROR_FUNCTION_FAILED;
6627 uirow = MSI_CreateRecord( 5 );
6628 MSI_RecordSetStringW( uirow, 1, desc );
6629 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6630 MSI_RecordSetInteger( uirow, 3, request );
6631 msi_ui_actiondata( package, szInstallODBC, uirow );
6632 msiobj_release( &uirow->hdr );
6634 msi_free(attrs);
6636 return r;
6639 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6641 static const WCHAR driver_query[] = {
6642 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6643 'O','D','B','C','D','r','i','v','e','r',0};
6644 static const WCHAR translator_query[] = {
6645 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6646 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6647 static const WCHAR source_query[] = {
6648 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6649 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6650 MSIQUERY *view;
6651 UINT rc;
6653 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6654 if (rc == ERROR_SUCCESS)
6656 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6657 msiobj_release(&view->hdr);
6658 if (rc != ERROR_SUCCESS)
6659 return rc;
6661 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6662 if (rc == ERROR_SUCCESS)
6664 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6665 msiobj_release(&view->hdr);
6666 if (rc != ERROR_SUCCESS)
6667 return rc;
6669 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6670 if (rc == ERROR_SUCCESS)
6672 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6673 msiobj_release(&view->hdr);
6674 if (rc != ERROR_SUCCESS)
6675 return rc;
6677 return ERROR_SUCCESS;
6680 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6682 MSIPACKAGE *package = param;
6683 MSICOMPONENT *comp;
6684 MSIRECORD *uirow;
6685 DWORD usage;
6686 LPCWSTR desc, component;
6688 component = MSI_RecordGetString( rec, 2 );
6689 comp = msi_get_loaded_component( package, component );
6690 if (!comp)
6691 return ERROR_SUCCESS;
6693 comp->Action = msi_get_component_action( package, comp );
6694 if (comp->Action != INSTALLSTATE_ABSENT)
6696 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6697 return ERROR_SUCCESS;
6700 desc = MSI_RecordGetString( rec, 3 );
6701 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6703 WARN("Failed to remove ODBC driver\n");
6705 else if (!usage)
6707 FIXME("Usage count reached 0\n");
6710 uirow = MSI_CreateRecord( 2 );
6711 MSI_RecordSetStringW( uirow, 1, desc );
6712 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6713 msi_ui_actiondata( package, szRemoveODBC, uirow );
6714 msiobj_release( &uirow->hdr );
6716 return ERROR_SUCCESS;
6719 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6721 MSIPACKAGE *package = param;
6722 MSICOMPONENT *comp;
6723 MSIRECORD *uirow;
6724 DWORD usage;
6725 LPCWSTR desc, component;
6727 component = MSI_RecordGetString( rec, 2 );
6728 comp = msi_get_loaded_component( package, component );
6729 if (!comp)
6730 return ERROR_SUCCESS;
6732 comp->Action = msi_get_component_action( package, comp );
6733 if (comp->Action != INSTALLSTATE_ABSENT)
6735 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6736 return ERROR_SUCCESS;
6739 desc = MSI_RecordGetString( rec, 3 );
6740 if (!SQLRemoveTranslatorW( desc, &usage ))
6742 WARN("Failed to remove ODBC translator\n");
6744 else if (!usage)
6746 FIXME("Usage count reached 0\n");
6749 uirow = MSI_CreateRecord( 2 );
6750 MSI_RecordSetStringW( uirow, 1, desc );
6751 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6752 msi_ui_actiondata( package, szRemoveODBC, uirow );
6753 msiobj_release( &uirow->hdr );
6755 return ERROR_SUCCESS;
6758 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6760 MSIPACKAGE *package = param;
6761 MSICOMPONENT *comp;
6762 MSIRECORD *uirow;
6763 LPWSTR attrs;
6764 LPCWSTR desc, driver, component;
6765 WORD request = ODBC_REMOVE_SYS_DSN;
6766 INT registration;
6767 DWORD len;
6769 static const WCHAR attrs_fmt[] = {
6770 'D','S','N','=','%','s',0 };
6772 component = MSI_RecordGetString( rec, 2 );
6773 comp = msi_get_loaded_component( package, component );
6774 if (!comp)
6775 return ERROR_SUCCESS;
6777 comp->Action = msi_get_component_action( package, comp );
6778 if (comp->Action != INSTALLSTATE_ABSENT)
6780 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6781 return ERROR_SUCCESS;
6784 desc = MSI_RecordGetString( rec, 3 );
6785 driver = MSI_RecordGetString( rec, 4 );
6786 registration = MSI_RecordGetInteger( rec, 5 );
6788 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6789 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6791 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6792 attrs = msi_alloc( len * sizeof(WCHAR) );
6793 if (!attrs)
6794 return ERROR_OUTOFMEMORY;
6796 FIXME("Use ODBCSourceAttribute table\n");
6798 len = sprintfW( attrs, attrs_fmt, desc );
6799 attrs[len + 1] = 0;
6801 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6803 WARN("Failed to remove ODBC data source\n");
6805 msi_free( attrs );
6807 uirow = MSI_CreateRecord( 3 );
6808 MSI_RecordSetStringW( uirow, 1, desc );
6809 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6810 MSI_RecordSetInteger( uirow, 3, request );
6811 msi_ui_actiondata( package, szRemoveODBC, uirow );
6812 msiobj_release( &uirow->hdr );
6814 return ERROR_SUCCESS;
6817 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6819 static const WCHAR driver_query[] = {
6820 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6821 'O','D','B','C','D','r','i','v','e','r',0};
6822 static const WCHAR translator_query[] = {
6823 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6824 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6825 static const WCHAR source_query[] = {
6826 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6827 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6828 MSIQUERY *view;
6829 UINT rc;
6831 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6832 if (rc == ERROR_SUCCESS)
6834 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6835 msiobj_release( &view->hdr );
6836 if (rc != ERROR_SUCCESS)
6837 return rc;
6839 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6840 if (rc == ERROR_SUCCESS)
6842 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6843 msiobj_release( &view->hdr );
6844 if (rc != ERROR_SUCCESS)
6845 return rc;
6847 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6848 if (rc == ERROR_SUCCESS)
6850 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6851 msiobj_release( &view->hdr );
6852 if (rc != ERROR_SUCCESS)
6853 return rc;
6855 return ERROR_SUCCESS;
6858 #define ENV_ACT_SETALWAYS 0x1
6859 #define ENV_ACT_SETABSENT 0x2
6860 #define ENV_ACT_REMOVE 0x4
6861 #define ENV_ACT_REMOVEMATCH 0x8
6863 #define ENV_MOD_MACHINE 0x20000000
6864 #define ENV_MOD_APPEND 0x40000000
6865 #define ENV_MOD_PREFIX 0x80000000
6866 #define ENV_MOD_MASK 0xC0000000
6868 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6870 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6872 LPCWSTR cptr = *name;
6874 static const WCHAR prefix[] = {'[','~',']',0};
6875 static const int prefix_len = 3;
6877 *flags = 0;
6878 while (*cptr)
6880 if (*cptr == '=')
6881 *flags |= ENV_ACT_SETALWAYS;
6882 else if (*cptr == '+')
6883 *flags |= ENV_ACT_SETABSENT;
6884 else if (*cptr == '-')
6885 *flags |= ENV_ACT_REMOVE;
6886 else if (*cptr == '!')
6887 *flags |= ENV_ACT_REMOVEMATCH;
6888 else if (*cptr == '*')
6889 *flags |= ENV_MOD_MACHINE;
6890 else
6891 break;
6893 cptr++;
6894 (*name)++;
6897 if (!*cptr)
6899 ERR("Missing environment variable\n");
6900 return ERROR_FUNCTION_FAILED;
6903 if (*value)
6905 LPCWSTR ptr = *value;
6906 if (!strncmpW(ptr, prefix, prefix_len))
6908 if (ptr[prefix_len] == szSemiColon[0])
6910 *flags |= ENV_MOD_APPEND;
6911 *value += lstrlenW(prefix);
6913 else
6915 *value = NULL;
6918 else if (lstrlenW(*value) >= prefix_len)
6920 ptr += lstrlenW(ptr) - prefix_len;
6921 if (!strcmpW( ptr, prefix ))
6923 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6925 *flags |= ENV_MOD_PREFIX;
6926 /* the "[~]" will be removed by deformat_string */;
6928 else
6930 *value = NULL;
6936 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6937 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6938 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6939 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6941 ERR("Invalid flags: %08x\n", *flags);
6942 return ERROR_FUNCTION_FAILED;
6945 if (!*flags)
6946 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6948 return ERROR_SUCCESS;
6951 static UINT open_env_key( DWORD flags, HKEY *key )
6953 static const WCHAR user_env[] =
6954 {'E','n','v','i','r','o','n','m','e','n','t',0};
6955 static const WCHAR machine_env[] =
6956 {'S','y','s','t','e','m','\\',
6957 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6958 'C','o','n','t','r','o','l','\\',
6959 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6960 'E','n','v','i','r','o','n','m','e','n','t',0};
6961 const WCHAR *env;
6962 HKEY root;
6963 LONG res;
6965 if (flags & ENV_MOD_MACHINE)
6967 env = machine_env;
6968 root = HKEY_LOCAL_MACHINE;
6970 else
6972 env = user_env;
6973 root = HKEY_CURRENT_USER;
6976 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6977 if (res != ERROR_SUCCESS)
6979 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6980 return ERROR_FUNCTION_FAILED;
6983 return ERROR_SUCCESS;
6986 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6988 MSIPACKAGE *package = param;
6989 LPCWSTR name, value, component;
6990 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
6991 DWORD flags, type, size, len, len_value = 0;
6992 UINT res;
6993 HKEY env = NULL;
6994 MSICOMPONENT *comp;
6995 MSIRECORD *uirow;
6996 int action = 0, found = 0;
6998 component = MSI_RecordGetString(rec, 4);
6999 comp = msi_get_loaded_component(package, component);
7000 if (!comp)
7001 return ERROR_SUCCESS;
7003 comp->Action = msi_get_component_action( package, comp );
7004 if (comp->Action != INSTALLSTATE_LOCAL)
7006 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7007 return ERROR_SUCCESS;
7009 name = MSI_RecordGetString(rec, 2);
7010 value = MSI_RecordGetString(rec, 3);
7012 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7014 res = env_parse_flags(&name, &value, &flags);
7015 if (res != ERROR_SUCCESS || !value)
7016 goto done;
7018 if (value && !deformat_string(package, value, &deformatted))
7020 res = ERROR_OUTOFMEMORY;
7021 goto done;
7024 if ((value = deformatted))
7026 if (flags & ENV_MOD_PREFIX)
7028 p = strrchrW( value, ';' );
7029 len_value = p - value;
7031 else if (flags & ENV_MOD_APPEND)
7033 value = strchrW( value, ';' ) + 1;
7034 len_value = strlenW( value );
7036 else len_value = strlenW( value );
7039 res = open_env_key( flags, &env );
7040 if (res != ERROR_SUCCESS)
7041 goto done;
7043 if (flags & ENV_MOD_MACHINE)
7044 action |= 0x20000000;
7046 size = 0;
7047 type = REG_SZ;
7048 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7049 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7050 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7051 goto done;
7053 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7055 action = 0x2;
7057 /* Nothing to do. */
7058 if (!value)
7060 res = ERROR_SUCCESS;
7061 goto done;
7063 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7064 newval = strdupW(value);
7065 if (!newval)
7067 res = ERROR_OUTOFMEMORY;
7068 goto done;
7071 else
7073 action = 0x1;
7075 /* Contrary to MSDN, +-variable to [~];path works */
7076 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7078 res = ERROR_SUCCESS;
7079 goto done;
7082 if (!(p = q = data = msi_alloc( size )))
7084 msi_free(deformatted);
7085 RegCloseKey(env);
7086 return ERROR_OUTOFMEMORY;
7089 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7090 if (res != ERROR_SUCCESS)
7091 goto done;
7093 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7095 action = 0x4;
7096 res = RegDeleteValueW(env, name);
7097 if (res != ERROR_SUCCESS)
7098 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7099 goto done;
7102 for (;;)
7104 while (*q && *q != ';') q++;
7105 len = q - p;
7106 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7107 (!p[len] || p[len] == ';'))
7109 found = 1;
7110 break;
7112 if (!*q) break;
7113 p = ++q;
7116 if (found)
7118 TRACE("string already set\n");
7119 goto done;
7122 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7123 if (!(p = newval = msi_alloc( size )))
7125 res = ERROR_OUTOFMEMORY;
7126 goto done;
7129 if (flags & ENV_MOD_PREFIX)
7131 memcpy( newval, value, len_value * sizeof(WCHAR) );
7132 newval[len_value] = ';';
7133 p = newval + len_value + 1;
7134 action |= 0x80000000;
7137 strcpyW( p, data );
7139 if (flags & ENV_MOD_APPEND)
7141 p += strlenW( data );
7142 *p++ = ';';
7143 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7144 action |= 0x40000000;
7147 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7148 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7149 if (res)
7151 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7154 done:
7155 uirow = MSI_CreateRecord( 3 );
7156 MSI_RecordSetStringW( uirow, 1, name );
7157 MSI_RecordSetStringW( uirow, 2, newval );
7158 MSI_RecordSetInteger( uirow, 3, action );
7159 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7160 msiobj_release( &uirow->hdr );
7162 if (env) RegCloseKey(env);
7163 msi_free(deformatted);
7164 msi_free(data);
7165 msi_free(newval);
7166 return res;
7169 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7171 static const WCHAR query[] = {
7172 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7173 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7174 MSIQUERY *view;
7175 UINT rc;
7177 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7178 if (rc != ERROR_SUCCESS)
7179 return ERROR_SUCCESS;
7181 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7182 msiobj_release(&view->hdr);
7183 return rc;
7186 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7188 MSIPACKAGE *package = param;
7189 LPCWSTR name, value, component;
7190 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7191 DWORD flags, type, size, len, len_value = 0, len_new_value;
7192 HKEY env;
7193 MSICOMPONENT *comp;
7194 MSIRECORD *uirow;
7195 int action = 0;
7196 LONG res;
7197 UINT r;
7199 component = MSI_RecordGetString( rec, 4 );
7200 comp = msi_get_loaded_component( package, component );
7201 if (!comp)
7202 return ERROR_SUCCESS;
7204 comp->Action = msi_get_component_action( package, comp );
7205 if (comp->Action != INSTALLSTATE_ABSENT)
7207 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7208 return ERROR_SUCCESS;
7210 name = MSI_RecordGetString( rec, 2 );
7211 value = MSI_RecordGetString( rec, 3 );
7213 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7215 r = env_parse_flags( &name, &value, &flags );
7216 if (r != ERROR_SUCCESS)
7217 return r;
7219 if (!(flags & ENV_ACT_REMOVE))
7221 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7222 return ERROR_SUCCESS;
7225 if (value && !deformat_string( package, value, &deformatted ))
7226 return ERROR_OUTOFMEMORY;
7228 if ((value = deformatted))
7230 if (flags & ENV_MOD_PREFIX)
7232 p = strchrW( value, ';' );
7233 len_value = p - value;
7235 else if (flags & ENV_MOD_APPEND)
7237 value = strchrW( value, ';' ) + 1;
7238 len_value = strlenW( value );
7240 else len_value = strlenW( value );
7243 r = open_env_key( flags, &env );
7244 if (r != ERROR_SUCCESS)
7246 r = ERROR_SUCCESS;
7247 goto done;
7250 if (flags & ENV_MOD_MACHINE)
7251 action |= 0x20000000;
7253 size = 0;
7254 type = REG_SZ;
7255 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7256 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7257 goto done;
7259 if (!(new_value = msi_alloc( size ))) goto done;
7261 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7262 if (res != ERROR_SUCCESS)
7263 goto done;
7265 len_new_value = size / sizeof(WCHAR) - 1;
7266 p = q = new_value;
7267 for (;;)
7269 while (*q && *q != ';') q++;
7270 len = q - p;
7271 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7273 if (*q == ';') q++;
7274 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7275 break;
7277 if (!*q) break;
7278 p = ++q;
7281 if (!new_value[0] || !value)
7283 TRACE("removing %s\n", debugstr_w(name));
7284 res = RegDeleteValueW( env, name );
7285 if (res != ERROR_SUCCESS)
7286 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7288 else
7290 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7291 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7292 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7293 if (res != ERROR_SUCCESS)
7294 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7297 done:
7298 uirow = MSI_CreateRecord( 3 );
7299 MSI_RecordSetStringW( uirow, 1, name );
7300 MSI_RecordSetStringW( uirow, 2, value );
7301 MSI_RecordSetInteger( uirow, 3, action );
7302 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7303 msiobj_release( &uirow->hdr );
7305 if (env) RegCloseKey( env );
7306 msi_free( deformatted );
7307 msi_free( new_value );
7308 return r;
7311 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7313 static const WCHAR query[] = {
7314 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7315 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7316 MSIQUERY *view;
7317 UINT rc;
7319 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7320 if (rc != ERROR_SUCCESS)
7321 return ERROR_SUCCESS;
7323 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7324 msiobj_release( &view->hdr );
7325 return rc;
7328 UINT msi_validate_product_id( MSIPACKAGE *package )
7330 LPWSTR key, template, id;
7331 UINT r = ERROR_SUCCESS;
7333 id = msi_dup_property( package->db, szProductID );
7334 if (id)
7336 msi_free( id );
7337 return ERROR_SUCCESS;
7339 template = msi_dup_property( package->db, szPIDTemplate );
7340 key = msi_dup_property( package->db, szPIDKEY );
7341 if (key && template)
7343 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7344 r = msi_set_property( package->db, szProductID, key, -1 );
7346 msi_free( template );
7347 msi_free( key );
7348 return r;
7351 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7353 return msi_validate_product_id( package );
7356 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7358 TRACE("\n");
7359 package->need_reboot_at_end = 1;
7360 return ERROR_SUCCESS;
7363 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7365 static const WCHAR szAvailableFreeReg[] =
7366 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7367 MSIRECORD *uirow;
7368 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7370 TRACE("%p %d kilobytes\n", package, space);
7372 uirow = MSI_CreateRecord( 1 );
7373 MSI_RecordSetInteger( uirow, 1, space );
7374 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7375 msiobj_release( &uirow->hdr );
7377 return ERROR_SUCCESS;
7380 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7382 TRACE("%p\n", package);
7384 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7385 return ERROR_SUCCESS;
7388 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7390 FIXME("%p\n", package);
7391 return ERROR_SUCCESS;
7394 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7396 static const WCHAR driver_query[] = {
7397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7398 'O','D','B','C','D','r','i','v','e','r',0};
7399 static const WCHAR translator_query[] = {
7400 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7401 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7402 MSIQUERY *view;
7403 UINT r, count;
7405 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7406 if (r == ERROR_SUCCESS)
7408 count = 0;
7409 r = MSI_IterateRecords( view, &count, NULL, package );
7410 msiobj_release( &view->hdr );
7411 if (r != ERROR_SUCCESS)
7412 return r;
7413 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7415 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7416 if (r == ERROR_SUCCESS)
7418 count = 0;
7419 r = MSI_IterateRecords( view, &count, NULL, package );
7420 msiobj_release( &view->hdr );
7421 if (r != ERROR_SUCCESS)
7422 return r;
7423 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7425 return ERROR_SUCCESS;
7428 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7430 static const WCHAR fmtW[] =
7431 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7432 'R','E','M','O','V','E','=','%','s',0};
7433 MSIPACKAGE *package = param;
7434 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7435 int attrs = MSI_RecordGetInteger( rec, 5 );
7436 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7437 WCHAR *product, *features, *cmd;
7438 STARTUPINFOW si;
7439 PROCESS_INFORMATION info;
7440 BOOL ret;
7442 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7443 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7445 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7447 len += strlenW( product );
7448 if (features)
7449 len += strlenW( features );
7450 else
7451 len += sizeof(szAll) / sizeof(szAll[0]);
7453 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7455 msi_free( product );
7456 msi_free( features );
7457 return ERROR_OUTOFMEMORY;
7459 sprintfW( cmd, fmtW, product, features ? features : szAll );
7460 msi_free( product );
7461 msi_free( features );
7463 memset( &si, 0, sizeof(STARTUPINFOW) );
7464 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7465 msi_free( cmd );
7466 if (!ret) return GetLastError();
7467 CloseHandle( info.hThread );
7469 WaitForSingleObject( info.hProcess, INFINITE );
7470 CloseHandle( info.hProcess );
7471 return ERROR_SUCCESS;
7474 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7476 static const WCHAR query[] = {
7477 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7478 MSIQUERY *view;
7479 UINT r;
7481 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7482 if (r == ERROR_SUCCESS)
7484 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7485 msiobj_release( &view->hdr );
7486 if (r != ERROR_SUCCESS)
7487 return r;
7489 return ERROR_SUCCESS;
7492 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7494 MSIPACKAGE *package = param;
7495 int attributes = MSI_RecordGetInteger( rec, 5 );
7497 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7499 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7500 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7501 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7502 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7503 HKEY hkey;
7504 UINT r;
7506 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7508 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7509 if (r != ERROR_SUCCESS)
7510 return ERROR_SUCCESS;
7512 else
7514 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7515 if (r != ERROR_SUCCESS)
7516 return ERROR_SUCCESS;
7518 RegCloseKey( hkey );
7520 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7521 debugstr_w(upgrade_code), debugstr_w(version_min),
7522 debugstr_w(version_max), debugstr_w(language));
7524 return ERROR_SUCCESS;
7527 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7529 static const WCHAR query[] = {
7530 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7531 'U','p','g','r','a','d','e',0};
7532 MSIQUERY *view;
7533 UINT r;
7535 if (msi_get_property_int( package->db, szInstalled, 0 ))
7537 TRACE("product is installed, skipping action\n");
7538 return ERROR_SUCCESS;
7540 if (msi_get_property_int( package->db, szPreselected, 0 ))
7542 TRACE("Preselected property is set, not migrating feature states\n");
7543 return ERROR_SUCCESS;
7545 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7546 if (r == ERROR_SUCCESS)
7548 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7549 msiobj_release( &view->hdr );
7550 if (r != ERROR_SUCCESS)
7551 return r;
7553 return ERROR_SUCCESS;
7556 static void bind_image( const char *filename, const char *path )
7558 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7560 WARN("failed to bind image %u\n", GetLastError());
7564 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7566 UINT i;
7567 MSIFILE *file;
7568 MSIPACKAGE *package = param;
7569 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7570 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7571 char *filenameA, *pathA;
7572 WCHAR *pathW, **path_list;
7574 if (!(file = msi_get_loaded_file( package, key )))
7576 WARN("file %s not found\n", debugstr_w(key));
7577 return ERROR_SUCCESS;
7579 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7580 path_list = msi_split_string( paths, ';' );
7581 if (!path_list) bind_image( filenameA, NULL );
7582 else
7584 for (i = 0; path_list[i] && path_list[i][0]; i++)
7586 deformat_string( package, path_list[i], &pathW );
7587 if ((pathA = strdupWtoA( pathW )))
7589 bind_image( filenameA, pathA );
7590 msi_free( pathA );
7592 msi_free( pathW );
7595 msi_free( path_list );
7596 msi_free( filenameA );
7597 return ERROR_SUCCESS;
7600 static UINT ACTION_BindImage( MSIPACKAGE *package )
7602 static const WCHAR query[] = {
7603 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7604 'B','i','n','d','I','m','a','g','e',0};
7605 MSIQUERY *view;
7606 UINT r;
7608 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7609 if (r == ERROR_SUCCESS)
7611 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7612 msiobj_release( &view->hdr );
7613 if (r != ERROR_SUCCESS)
7614 return r;
7616 return ERROR_SUCCESS;
7619 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7621 static const WCHAR query[] = {
7622 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7623 MSIQUERY *view;
7624 DWORD count = 0;
7625 UINT r;
7627 r = MSI_OpenQuery( package->db, &view, query, table );
7628 if (r == ERROR_SUCCESS)
7630 r = MSI_IterateRecords(view, &count, NULL, package);
7631 msiobj_release(&view->hdr);
7632 if (r != ERROR_SUCCESS)
7633 return r;
7635 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7636 return ERROR_SUCCESS;
7639 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7641 static const WCHAR table[] = {
7642 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7643 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7646 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7648 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7649 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7652 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7654 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7655 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7658 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7660 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7661 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7664 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7666 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7667 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7670 static const struct
7672 const WCHAR *action;
7673 UINT (*handler)(MSIPACKAGE *);
7674 const WCHAR *action_rollback;
7676 StandardActions[] =
7678 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7679 { szAppSearch, ACTION_AppSearch, NULL },
7680 { szBindImage, ACTION_BindImage, NULL },
7681 { szCCPSearch, ACTION_CCPSearch, NULL },
7682 { szCostFinalize, ACTION_CostFinalize, NULL },
7683 { szCostInitialize, ACTION_CostInitialize, NULL },
7684 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7685 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7686 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7687 { szDisableRollback, ACTION_DisableRollback, NULL },
7688 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7689 { szExecuteAction, ACTION_ExecuteAction, NULL },
7690 { szFileCost, ACTION_FileCost, NULL },
7691 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7692 { szForceReboot, ACTION_ForceReboot, NULL },
7693 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7694 { szInstallExecute, ACTION_InstallExecute, NULL },
7695 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7696 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7697 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7698 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7699 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7700 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7701 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7702 { szInstallValidate, ACTION_InstallValidate, NULL },
7703 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7704 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7705 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7706 { szMoveFiles, ACTION_MoveFiles, NULL },
7707 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7708 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7709 { szPatchFiles, ACTION_PatchFiles, NULL },
7710 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7711 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7712 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7713 { szPublishProduct, ACTION_PublishProduct, szUnpublishProduct },
7714 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7715 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7716 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7717 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7718 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7719 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7720 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7721 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7722 { szRegisterUser, ACTION_RegisterUser, NULL },
7723 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7724 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7725 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7726 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7727 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7728 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7729 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7730 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7731 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7732 { szResolveSource, ACTION_ResolveSource, NULL },
7733 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7734 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7735 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7736 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7737 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7738 { szStartServices, ACTION_StartServices, szStopServices },
7739 { szStopServices, ACTION_StopServices, szStartServices },
7740 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7741 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7742 { szUnpublishProduct, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7743 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7744 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7745 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7746 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7747 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7748 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7749 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7750 { szValidateProductID, ACTION_ValidateProductID, NULL },
7751 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7752 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7753 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7754 { NULL, NULL, NULL }
7757 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7759 BOOL ret = FALSE;
7760 UINT i;
7762 i = 0;
7763 while (StandardActions[i].action != NULL)
7765 if (!strcmpW( StandardActions[i].action, action ))
7767 ui_actionstart( package, action );
7768 if (StandardActions[i].handler)
7770 ui_actioninfo( package, action, TRUE, 0 );
7771 *rc = StandardActions[i].handler( package );
7772 ui_actioninfo( package, action, FALSE, *rc );
7774 if (StandardActions[i].action_rollback && !package->need_rollback)
7776 TRACE("scheduling rollback action\n");
7777 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7780 else
7782 FIXME("unhandled standard action %s\n", debugstr_w(action));
7783 *rc = ERROR_SUCCESS;
7785 ret = TRUE;
7786 break;
7788 i++;
7790 return ret;
7793 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7795 UINT rc = ERROR_SUCCESS;
7796 BOOL handled;
7798 TRACE("Performing action (%s)\n", debugstr_w(action));
7800 handled = ACTION_HandleStandardAction(package, action, &rc);
7802 if (!handled)
7803 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7805 if (!handled)
7807 WARN("unhandled msi action %s\n", debugstr_w(action));
7808 rc = ERROR_FUNCTION_NOT_CALLED;
7811 return rc;
7814 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7816 UINT rc = ERROR_SUCCESS;
7817 BOOL handled = FALSE;
7819 TRACE("Performing action (%s)\n", debugstr_w(action));
7821 package->action_progress_increment = 0;
7822 handled = ACTION_HandleStandardAction(package, action, &rc);
7824 if (!handled)
7825 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7827 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7828 handled = TRUE;
7830 if (!handled)
7832 WARN("unhandled msi action %s\n", debugstr_w(action));
7833 rc = ERROR_FUNCTION_NOT_CALLED;
7836 return rc;
7839 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7841 UINT rc = ERROR_SUCCESS;
7842 MSIRECORD *row;
7844 static const WCHAR query[] =
7845 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7846 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7847 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7848 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7849 static const WCHAR ui_query[] =
7850 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7851 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7852 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7853 ' ', '=',' ','%','i',0};
7855 if (needs_ui_sequence(package))
7856 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7857 else
7858 row = MSI_QueryGetRecord(package->db, query, seq);
7860 if (row)
7862 LPCWSTR action, cond;
7864 TRACE("Running the actions\n");
7866 /* check conditions */
7867 cond = MSI_RecordGetString(row, 2);
7869 /* this is a hack to skip errors in the condition code */
7870 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7872 msiobj_release(&row->hdr);
7873 return ERROR_SUCCESS;
7876 action = MSI_RecordGetString(row, 1);
7877 if (!action)
7879 ERR("failed to fetch action\n");
7880 msiobj_release(&row->hdr);
7881 return ERROR_FUNCTION_FAILED;
7884 if (needs_ui_sequence(package))
7885 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7886 else
7887 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7889 msiobj_release(&row->hdr);
7892 return rc;
7895 /****************************************************
7896 * TOP level entry points
7897 *****************************************************/
7899 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7900 LPCWSTR szCommandLine )
7902 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7903 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7904 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7905 WCHAR *reinstall, *remove, *patch, *productcode;
7906 BOOL ui_exists;
7907 UINT rc;
7909 msi_set_property( package->db, szAction, szInstall, -1 );
7911 package->script->InWhatSequence = SEQUENCE_INSTALL;
7913 if (szPackagePath)
7915 LPWSTR p, dir;
7916 LPCWSTR file;
7918 dir = strdupW(szPackagePath);
7919 p = strrchrW(dir, '\\');
7920 if (p)
7922 *(++p) = 0;
7923 file = szPackagePath + (p - dir);
7925 else
7927 msi_free(dir);
7928 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7929 GetCurrentDirectoryW(MAX_PATH, dir);
7930 lstrcatW(dir, szBackSlash);
7931 file = szPackagePath;
7934 msi_free( package->PackagePath );
7935 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7936 if (!package->PackagePath)
7938 msi_free(dir);
7939 return ERROR_OUTOFMEMORY;
7942 lstrcpyW(package->PackagePath, dir);
7943 lstrcatW(package->PackagePath, file);
7944 msi_free(dir);
7946 msi_set_sourcedir_props(package, FALSE);
7949 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7950 if (rc != ERROR_SUCCESS)
7951 return rc;
7953 msi_apply_transforms( package );
7954 msi_apply_patches( package );
7956 patch = msi_dup_property( package->db, szPatch );
7957 remove = msi_dup_property( package->db, szRemove );
7958 reinstall = msi_dup_property( package->db, szReinstall );
7959 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7961 TRACE("setting REINSTALL property to ALL\n");
7962 msi_set_property( package->db, szReinstall, szAll, -1 );
7963 package->full_reinstall = 1;
7966 msi_set_original_database_property( package->db, szPackagePath );
7967 msi_parse_command_line( package, szCommandLine, FALSE );
7968 msi_adjust_privilege_properties( package );
7969 msi_set_context( package );
7971 productcode = msi_dup_property( package->db, szProductCode );
7972 if (strcmpiW( productcode, package->ProductCode ))
7974 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
7975 msi_free( package->ProductCode );
7976 package->ProductCode = productcode;
7978 else msi_free( productcode );
7980 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7982 TRACE("disabling rollback\n");
7983 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7986 if (needs_ui_sequence( package))
7988 package->script->InWhatSequence |= SEQUENCE_UI;
7989 rc = ACTION_ProcessUISequence(package);
7990 ui_exists = ui_sequence_exists(package);
7991 if (rc == ERROR_SUCCESS || !ui_exists)
7993 package->script->InWhatSequence |= SEQUENCE_EXEC;
7994 rc = ACTION_ProcessExecSequence(package, ui_exists);
7997 else
7998 rc = ACTION_ProcessExecSequence(package, FALSE);
8000 /* process the ending type action */
8001 if (rc == ERROR_SUCCESS)
8002 ACTION_PerformActionSequence(package, -1);
8003 else if (rc == ERROR_INSTALL_USEREXIT)
8004 ACTION_PerformActionSequence(package, -2);
8005 else if (rc == ERROR_INSTALL_SUSPEND)
8006 ACTION_PerformActionSequence(package, -4);
8007 else /* failed */
8009 ACTION_PerformActionSequence(package, -3);
8010 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8012 package->need_rollback = TRUE;
8016 /* finish up running custom actions */
8017 ACTION_FinishCustomActions(package);
8019 if (package->need_rollback && !reinstall)
8021 WARN("installation failed, running rollback script\n");
8022 execute_script( package, SCRIPT_ROLLBACK );
8024 msi_free( reinstall );
8025 msi_free( remove );
8026 msi_free( patch );
8028 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8029 return ERROR_SUCCESS_REBOOT_REQUIRED;
8031 return rc;