msi: Clean up the database queries.
[wine/multimedia.git] / dlls / msi / action.c
blobacc624b048c3df94c2fea8f523572912a189a4c7
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 szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 if (!count) goto done;
226 in_quotes = 1;
227 ignore = 1;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes && p[1] != '\"') count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 if (!count) in_quotes = 0;
238 else in_quotes = 1;
239 len++;
240 break;
242 break;
244 case state_token:
245 switch (*p)
247 case '"':
248 state = state_quote;
249 if (in_quotes) count--;
250 else count++;
251 break;
252 case ' ':
253 state = state_whitespace;
254 if (!count) goto done;
255 in_quotes = 1;
256 len++;
257 break;
258 default:
259 if (!count) in_quotes = 0;
260 else in_quotes = 1;
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 = 1;
277 len++;
278 break;
279 default:
280 state = state_token;
281 if (!count) in_quotes = 0;
282 else in_quotes = 1;
283 len++;
284 break;
286 break;
288 default: break;
290 if (!ignore) *out++ = *p;
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 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
338 memcpy( prop, ptr, len * sizeof(WCHAR) );
339 prop[len] = 0;
340 if (!preserve_case) struprW( prop );
342 ptr2++;
343 while (*ptr2 == ' ') ptr2++;
345 num_quotes = 0;
346 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
347 len = parse_prop( ptr2, val, &num_quotes );
348 if (num_quotes % 2)
350 WARN("unbalanced quotes\n");
351 msi_free( val );
352 msi_free( prop );
353 return ERROR_INVALID_COMMAND_LINE;
355 remove_quotes( val );
356 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
358 r = msi_set_property( package->db, prop, val );
359 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
360 msi_reset_folders( package, TRUE );
362 msi_free( val );
363 msi_free( prop );
365 ptr = ptr2 + len;
368 return ERROR_SUCCESS;
371 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
373 LPCWSTR pc;
374 LPWSTR p, *ret = NULL;
375 UINT count = 0;
377 if (!str)
378 return ret;
380 /* count the number of substrings */
381 for ( pc = str, count = 0; pc; count++ )
383 pc = strchrW( pc, sep );
384 if (pc)
385 pc++;
388 /* allocate space for an array of substring pointers and the substrings */
389 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
390 (lstrlenW(str)+1) * sizeof(WCHAR) );
391 if (!ret)
392 return ret;
394 /* copy the string and set the pointers */
395 p = (LPWSTR) &ret[count+1];
396 lstrcpyW( p, str );
397 for( count = 0; (ret[count] = p); count++ )
399 p = strchrW( p, sep );
400 if (p)
401 *p++ = 0;
404 return ret;
407 static BOOL ui_sequence_exists( MSIPACKAGE *package )
409 static const WCHAR query [] = {
410 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
411 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
412 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
413 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
414 MSIQUERY *view;
415 UINT rc;
417 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
418 if (rc == ERROR_SUCCESS)
420 msiobj_release(&view->hdr);
421 return TRUE;
423 return FALSE;
426 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
428 LPWSTR source, check;
430 if (msi_get_property_int( package->db, szInstalled, 0 ))
432 HKEY hkey;
434 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
435 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
436 RegCloseKey( hkey );
438 else
440 LPWSTR p, db;
441 DWORD len;
443 db = msi_dup_property( package->db, szOriginalDatabase );
444 if (!db)
445 return ERROR_OUTOFMEMORY;
447 p = strrchrW( db, '\\' );
448 if (!p)
450 p = strrchrW( db, '/' );
451 if (!p)
453 msi_free(db);
454 return ERROR_SUCCESS;
458 len = p - db + 2;
459 source = msi_alloc( len * sizeof(WCHAR) );
460 lstrcpynW( source, db, len );
461 msi_free( db );
464 check = msi_dup_property( package->db, szSourceDir );
465 if (!check || replace)
467 UINT r = msi_set_property( package->db, szSourceDir, source );
468 if (r == ERROR_SUCCESS)
469 msi_reset_folders( package, TRUE );
471 msi_free( check );
473 check = msi_dup_property( package->db, szSOURCEDIR );
474 if (!check || replace)
475 msi_set_property( package->db, szSOURCEDIR, source );
477 msi_free( check );
478 msi_free( source );
480 return ERROR_SUCCESS;
483 static BOOL needs_ui_sequence(MSIPACKAGE *package)
485 INT level = msi_get_property_int(package->db, szUILevel, 0);
486 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
489 UINT msi_set_context(MSIPACKAGE *package)
491 int num;
493 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
495 num = msi_get_property_int(package->db, szAllUsers, 0);
496 if (num == 1 || num == 2)
497 package->Context = MSIINSTALLCONTEXT_MACHINE;
499 return ERROR_SUCCESS;
502 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
504 UINT rc;
505 LPCWSTR cond, action;
506 MSIPACKAGE *package = param;
508 action = MSI_RecordGetString(row,1);
509 if (!action)
511 ERR("Error is retrieving action name\n");
512 return ERROR_FUNCTION_FAILED;
515 /* check conditions */
516 cond = MSI_RecordGetString(row,2);
518 /* this is a hack to skip errors in the condition code */
519 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
521 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
522 return ERROR_SUCCESS;
525 if (needs_ui_sequence(package))
526 rc = ACTION_PerformUIAction(package, action, -1);
527 else
528 rc = ACTION_PerformAction(package, action, -1);
530 msi_dialog_check_messages( NULL );
532 if (package->CurrentInstallState != ERROR_SUCCESS)
533 rc = package->CurrentInstallState;
535 if (rc == ERROR_FUNCTION_NOT_CALLED)
536 rc = ERROR_SUCCESS;
538 if (rc != ERROR_SUCCESS)
539 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
541 return rc;
544 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
546 static const WCHAR query[] = {
547 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
548 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
549 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
550 '`','S','e','q','u','e','n','c','e','`',0};
551 MSIQUERY *view;
552 UINT r;
554 TRACE("%p %s\n", package, debugstr_w(table));
556 r = MSI_OpenQuery( package->db, &view, query, table );
557 if (r == ERROR_SUCCESS)
559 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
560 msiobj_release(&view->hdr);
562 return r;
565 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
567 static const WCHAR query[] = {
568 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
569 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
570 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
571 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
572 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
573 static const WCHAR query_validate[] = {
574 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
575 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
576 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
577 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
578 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
579 MSIQUERY *view;
580 INT seq = 0;
581 UINT rc;
583 if (package->script->ExecuteSequenceRun)
585 TRACE("Execute Sequence already Run\n");
586 return ERROR_SUCCESS;
589 package->script->ExecuteSequenceRun = TRUE;
591 /* get the sequence number */
592 if (UIran)
594 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
595 if (!row) return ERROR_FUNCTION_FAILED;
596 seq = MSI_RecordGetInteger(row,1);
597 msiobj_release(&row->hdr);
599 rc = MSI_OpenQuery(package->db, &view, query, seq);
600 if (rc == ERROR_SUCCESS)
602 TRACE("Running the actions\n");
604 msi_set_property(package->db, szSourceDir, NULL);
605 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
606 msiobj_release(&view->hdr);
608 return rc;
611 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
613 static const WCHAR query[] = {
614 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
615 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
616 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
617 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
618 MSIQUERY *view;
619 UINT rc;
621 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
622 if (rc == ERROR_SUCCESS)
624 TRACE("Running the actions\n");
625 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
626 msiobj_release(&view->hdr);
628 return rc;
631 /********************************************************
632 * ACTION helper functions and functions that perform the actions
633 *******************************************************/
634 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
635 UINT* rc, UINT script, BOOL force )
637 BOOL ret=FALSE;
638 UINT arc;
640 arc = ACTION_CustomAction(package, action, script, force);
642 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
644 *rc = arc;
645 ret = TRUE;
647 return ret;
650 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
652 MSICOMPONENT *comp;
654 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
656 if (!strcmpW( Component, comp->Component )) return comp;
658 return NULL;
661 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
663 MSIFEATURE *feature;
665 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
667 if (!strcmpW( Feature, feature->Feature )) return feature;
669 return NULL;
672 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
674 MSIFILE *file;
676 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
678 if (!strcmpW( key, file->File )) return file;
680 return NULL;
683 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
685 MSIFILEPATCH *patch;
687 /* FIXME: There might be more than one patch */
688 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
690 if (!strcmpW( key, patch->File->File )) return patch;
692 return NULL;
695 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
697 MSIFOLDER *folder;
699 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
701 if (!strcmpW( dir, folder->Directory )) return folder;
703 return NULL;
707 * Recursively create all directories in the path.
708 * shamelessly stolen from setupapi/queue.c
710 BOOL msi_create_full_path( const WCHAR *path )
712 BOOL ret = TRUE;
713 WCHAR *new_path;
714 int len;
716 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
717 strcpyW( new_path, path );
719 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
720 new_path[len - 1] = 0;
722 while (!CreateDirectoryW( new_path, NULL ))
724 WCHAR *slash;
725 DWORD last_error = GetLastError();
726 if (last_error == ERROR_ALREADY_EXISTS) break;
727 if (last_error != ERROR_PATH_NOT_FOUND)
729 ret = FALSE;
730 break;
732 if (!(slash = strrchrW( new_path, '\\' )))
734 ret = FALSE;
735 break;
737 len = slash - new_path;
738 new_path[len] = 0;
739 if (!msi_create_full_path( new_path ))
741 ret = FALSE;
742 break;
744 new_path[len] = '\\';
746 msi_free( new_path );
747 return ret;
750 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
752 MSIRECORD *row;
754 row = MSI_CreateRecord( 4 );
755 MSI_RecordSetInteger( row, 1, a );
756 MSI_RecordSetInteger( row, 2, b );
757 MSI_RecordSetInteger( row, 3, c );
758 MSI_RecordSetInteger( row, 4, d );
759 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
760 msiobj_release( &row->hdr );
762 msi_dialog_check_messages( NULL );
765 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
767 static const WCHAR query[] =
768 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
769 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
770 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
771 WCHAR message[1024];
772 MSIRECORD *row = 0;
773 DWORD size;
775 if (!package->LastAction || strcmpW( package->LastAction, action ))
777 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
779 if (MSI_RecordIsNull( row, 3 ))
781 msiobj_release( &row->hdr );
782 return;
784 /* update the cached action format */
785 msi_free( package->ActionFormat );
786 package->ActionFormat = msi_dup_record_field( row, 3 );
787 msi_free( package->LastAction );
788 package->LastAction = strdupW( action );
789 msiobj_release( &row->hdr );
791 size = 1024;
792 MSI_RecordSetStringW( record, 0, package->ActionFormat );
793 MSI_FormatRecordW( package, record, message, &size );
794 row = MSI_CreateRecord( 1 );
795 MSI_RecordSetStringW( row, 1, message );
796 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
797 msiobj_release( &row->hdr );
800 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
802 if (!comp->Enabled)
804 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
805 return INSTALLSTATE_UNKNOWN;
807 if (package->need_rollback) return comp->Installed;
808 return comp->ActionRequest;
811 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
813 if (package->need_rollback) return feature->Installed;
814 return feature->ActionRequest;
817 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
819 MSIPACKAGE *package = param;
820 LPCWSTR dir, component, full_path;
821 MSIRECORD *uirow;
822 MSIFOLDER *folder;
823 MSICOMPONENT *comp;
825 component = MSI_RecordGetString(row, 2);
826 if (!component)
827 return ERROR_SUCCESS;
829 comp = msi_get_loaded_component(package, component);
830 if (!comp)
831 return ERROR_SUCCESS;
833 comp->Action = msi_get_component_action( package, comp );
834 if (comp->Action != INSTALLSTATE_LOCAL)
836 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
837 return ERROR_SUCCESS;
840 dir = MSI_RecordGetString(row,1);
841 if (!dir)
843 ERR("Unable to get folder id\n");
844 return ERROR_SUCCESS;
847 uirow = MSI_CreateRecord(1);
848 MSI_RecordSetStringW(uirow, 1, dir);
849 msi_ui_actiondata(package, szCreateFolders, uirow);
850 msiobj_release(&uirow->hdr);
852 full_path = msi_get_target_folder( package, dir );
853 if (!full_path)
855 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
856 return ERROR_SUCCESS;
858 TRACE("folder is %s\n", debugstr_w(full_path));
860 folder = msi_get_loaded_folder( package, dir );
861 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
862 folder->State = FOLDER_STATE_CREATED;
863 return ERROR_SUCCESS;
866 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
868 static const WCHAR query[] = {
869 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
870 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
871 MSIQUERY *view;
872 UINT rc;
874 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
875 if (rc != ERROR_SUCCESS)
876 return ERROR_SUCCESS;
878 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
879 msiobj_release(&view->hdr);
880 return rc;
883 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
885 MSIPACKAGE *package = param;
886 LPCWSTR dir, component, full_path;
887 MSIRECORD *uirow;
888 MSIFOLDER *folder;
889 MSICOMPONENT *comp;
891 component = MSI_RecordGetString(row, 2);
892 if (!component)
893 return ERROR_SUCCESS;
895 comp = msi_get_loaded_component(package, component);
896 if (!comp)
897 return ERROR_SUCCESS;
899 comp->Action = msi_get_component_action( package, comp );
900 if (comp->Action != INSTALLSTATE_ABSENT)
902 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
903 return ERROR_SUCCESS;
906 dir = MSI_RecordGetString( row, 1 );
907 if (!dir)
909 ERR("Unable to get folder id\n");
910 return ERROR_SUCCESS;
913 full_path = msi_get_target_folder( package, dir );
914 if (!full_path)
916 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
917 return ERROR_SUCCESS;
919 TRACE("folder is %s\n", debugstr_w(full_path));
921 uirow = MSI_CreateRecord( 1 );
922 MSI_RecordSetStringW( uirow, 1, dir );
923 msi_ui_actiondata( package, szRemoveFolders, uirow );
924 msiobj_release( &uirow->hdr );
926 RemoveDirectoryW( full_path );
927 folder = msi_get_loaded_folder( package, dir );
928 folder->State = FOLDER_STATE_REMOVED;
929 return ERROR_SUCCESS;
932 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
934 static const WCHAR query[] = {
935 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
936 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
937 MSIQUERY *view;
938 UINT rc;
940 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
941 if (rc != ERROR_SUCCESS)
942 return ERROR_SUCCESS;
944 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
945 msiobj_release( &view->hdr );
946 return rc;
949 static UINT load_component( MSIRECORD *row, LPVOID param )
951 MSIPACKAGE *package = param;
952 MSICOMPONENT *comp;
954 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
955 if (!comp)
956 return ERROR_FUNCTION_FAILED;
958 list_add_tail( &package->components, &comp->entry );
960 /* fill in the data */
961 comp->Component = msi_dup_record_field( row, 1 );
963 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
965 comp->ComponentId = msi_dup_record_field( row, 2 );
966 comp->Directory = msi_dup_record_field( row, 3 );
967 comp->Attributes = MSI_RecordGetInteger(row,4);
968 comp->Condition = msi_dup_record_field( row, 5 );
969 comp->KeyPath = msi_dup_record_field( row, 6 );
971 comp->Installed = INSTALLSTATE_UNKNOWN;
972 comp->Action = INSTALLSTATE_UNKNOWN;
973 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
975 comp->assembly = msi_load_assembly( package, comp );
976 return ERROR_SUCCESS;
979 UINT msi_load_all_components( MSIPACKAGE *package )
981 static const WCHAR query[] = {
982 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
983 '`','C','o','m','p','o','n','e','n','t','`',0};
984 MSIQUERY *view;
985 UINT r;
987 if (!list_empty(&package->components))
988 return ERROR_SUCCESS;
990 r = MSI_DatabaseOpenViewW( package->db, query, &view );
991 if (r != ERROR_SUCCESS)
992 return r;
994 if (!msi_init_assembly_caches( package ))
996 ERR("can't initialize assembly caches\n");
997 msiobj_release( &view->hdr );
998 return ERROR_FUNCTION_FAILED;
1001 r = MSI_IterateRecords(view, NULL, load_component, package);
1002 msiobj_release(&view->hdr);
1003 msi_destroy_assembly_caches( package );
1004 return r;
1007 typedef struct {
1008 MSIPACKAGE *package;
1009 MSIFEATURE *feature;
1010 } _ilfs;
1012 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1014 ComponentList *cl;
1016 cl = msi_alloc( sizeof (*cl) );
1017 if ( !cl )
1018 return ERROR_NOT_ENOUGH_MEMORY;
1019 cl->component = comp;
1020 list_add_tail( &feature->Components, &cl->entry );
1022 return ERROR_SUCCESS;
1025 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1027 FeatureList *fl;
1029 fl = msi_alloc( sizeof(*fl) );
1030 if ( !fl )
1031 return ERROR_NOT_ENOUGH_MEMORY;
1032 fl->feature = child;
1033 list_add_tail( &parent->Children, &fl->entry );
1035 return ERROR_SUCCESS;
1038 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1040 _ilfs* ilfs = param;
1041 LPCWSTR component;
1042 MSICOMPONENT *comp;
1044 component = MSI_RecordGetString(row,1);
1046 /* check to see if the component is already loaded */
1047 comp = msi_get_loaded_component( ilfs->package, component );
1048 if (!comp)
1050 ERR("unknown component %s\n", debugstr_w(component));
1051 return ERROR_FUNCTION_FAILED;
1054 add_feature_component( ilfs->feature, comp );
1055 comp->Enabled = TRUE;
1057 return ERROR_SUCCESS;
1060 static UINT load_feature(MSIRECORD * row, LPVOID param)
1062 static const WCHAR query[] = {
1063 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1064 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1065 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1066 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1067 MSIPACKAGE *package = param;
1068 MSIFEATURE *feature;
1069 MSIQUERY *view;
1070 _ilfs ilfs;
1071 UINT rc;
1073 /* fill in the data */
1075 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1076 if (!feature)
1077 return ERROR_NOT_ENOUGH_MEMORY;
1079 list_init( &feature->Children );
1080 list_init( &feature->Components );
1082 feature->Feature = msi_dup_record_field( row, 1 );
1084 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1086 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1087 feature->Title = msi_dup_record_field( row, 3 );
1088 feature->Description = msi_dup_record_field( row, 4 );
1090 if (!MSI_RecordIsNull(row,5))
1091 feature->Display = MSI_RecordGetInteger(row,5);
1093 feature->Level= MSI_RecordGetInteger(row,6);
1094 feature->Directory = msi_dup_record_field( row, 7 );
1095 feature->Attributes = MSI_RecordGetInteger(row,8);
1097 feature->Installed = INSTALLSTATE_UNKNOWN;
1098 feature->Action = INSTALLSTATE_UNKNOWN;
1099 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1101 list_add_tail( &package->features, &feature->entry );
1103 /* load feature components */
1105 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1106 if (rc != ERROR_SUCCESS)
1107 return ERROR_SUCCESS;
1109 ilfs.package = package;
1110 ilfs.feature = feature;
1112 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1113 msiobj_release(&view->hdr);
1114 return ERROR_SUCCESS;
1117 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1119 MSIPACKAGE *package = param;
1120 MSIFEATURE *parent, *child;
1122 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1123 if (!child)
1124 return ERROR_FUNCTION_FAILED;
1126 if (!child->Feature_Parent)
1127 return ERROR_SUCCESS;
1129 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1130 if (!parent)
1131 return ERROR_FUNCTION_FAILED;
1133 add_feature_child( parent, child );
1134 return ERROR_SUCCESS;
1137 UINT msi_load_all_features( MSIPACKAGE *package )
1139 static const WCHAR query[] = {
1140 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1141 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1142 '`','D','i','s','p','l','a','y','`',0};
1143 MSIQUERY *view;
1144 UINT r;
1146 if (!list_empty(&package->features))
1147 return ERROR_SUCCESS;
1149 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1150 if (r != ERROR_SUCCESS)
1151 return r;
1153 r = MSI_IterateRecords( view, NULL, load_feature, package );
1154 if (r != ERROR_SUCCESS)
1156 msiobj_release( &view->hdr );
1157 return r;
1159 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1160 msiobj_release( &view->hdr );
1161 return r;
1164 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1166 if (!p)
1167 return p;
1168 p = strchrW(p, ch);
1169 if (!p)
1170 return p;
1171 *p = 0;
1172 return p+1;
1175 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1177 static const WCHAR query[] = {
1178 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1179 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1180 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1181 MSIQUERY *view = NULL;
1182 MSIRECORD *row = NULL;
1183 UINT r;
1185 TRACE("%s\n", debugstr_w(file->File));
1187 r = MSI_OpenQuery(package->db, &view, query, file->File);
1188 if (r != ERROR_SUCCESS)
1189 goto done;
1191 r = MSI_ViewExecute(view, NULL);
1192 if (r != ERROR_SUCCESS)
1193 goto done;
1195 r = MSI_ViewFetch(view, &row);
1196 if (r != ERROR_SUCCESS)
1197 goto done;
1199 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1200 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1201 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1202 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1203 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1205 done:
1206 if (view) msiobj_release(&view->hdr);
1207 if (row) msiobj_release(&row->hdr);
1208 return r;
1211 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1213 MSIRECORD *row;
1214 static const WCHAR query[] = {
1215 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1216 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1217 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1219 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1220 if (!row)
1222 WARN("query failed\n");
1223 return ERROR_FUNCTION_FAILED;
1226 file->disk_id = MSI_RecordGetInteger( row, 1 );
1227 msiobj_release( &row->hdr );
1228 return ERROR_SUCCESS;
1231 static UINT load_file(MSIRECORD *row, LPVOID param)
1233 MSIPACKAGE* package = param;
1234 LPCWSTR component;
1235 MSIFILE *file;
1237 /* fill in the data */
1239 file = msi_alloc_zero( sizeof (MSIFILE) );
1240 if (!file)
1241 return ERROR_NOT_ENOUGH_MEMORY;
1243 file->File = msi_dup_record_field( row, 1 );
1245 component = MSI_RecordGetString( row, 2 );
1246 file->Component = msi_get_loaded_component( package, component );
1248 if (!file->Component)
1250 WARN("Component not found: %s\n", debugstr_w(component));
1251 msi_free(file->File);
1252 msi_free(file);
1253 return ERROR_SUCCESS;
1256 file->FileName = msi_dup_record_field( row, 3 );
1257 msi_reduce_to_long_filename( file->FileName );
1259 file->ShortName = msi_dup_record_field( row, 3 );
1260 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1262 file->FileSize = MSI_RecordGetInteger( row, 4 );
1263 file->Version = msi_dup_record_field( row, 5 );
1264 file->Language = msi_dup_record_field( row, 6 );
1265 file->Attributes = MSI_RecordGetInteger( row, 7 );
1266 file->Sequence = MSI_RecordGetInteger( row, 8 );
1268 file->state = msifs_invalid;
1270 /* if the compressed bits are not set in the file attributes,
1271 * then read the information from the package word count property
1273 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1275 file->IsCompressed = FALSE;
1277 else if (file->Attributes &
1278 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1280 file->IsCompressed = TRUE;
1282 else if (file->Attributes & msidbFileAttributesNoncompressed)
1284 file->IsCompressed = FALSE;
1286 else
1288 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1291 load_file_hash(package, file);
1292 load_file_disk_id(package, file);
1294 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1296 list_add_tail( &package->files, &file->entry );
1298 return ERROR_SUCCESS;
1301 static UINT load_all_files(MSIPACKAGE *package)
1303 static const WCHAR query[] = {
1304 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1305 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1306 '`','S','e','q','u','e','n','c','e','`', 0};
1307 MSIQUERY *view;
1308 UINT rc;
1310 if (!list_empty(&package->files))
1311 return ERROR_SUCCESS;
1313 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1314 if (rc != ERROR_SUCCESS)
1315 return ERROR_SUCCESS;
1317 rc = MSI_IterateRecords(view, NULL, load_file, package);
1318 msiobj_release(&view->hdr);
1319 return ERROR_SUCCESS;
1322 static UINT load_media( MSIRECORD *row, LPVOID param )
1324 MSIPACKAGE *package = param;
1325 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1326 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1328 /* FIXME: load external cabinets and directory sources too */
1329 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1330 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1331 return ERROR_SUCCESS;
1334 static UINT load_all_media( MSIPACKAGE *package )
1336 static const WCHAR query[] = {
1337 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1338 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1339 '`','D','i','s','k','I','d','`',0};
1340 MSIQUERY *view;
1341 UINT r;
1343 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1344 if (r != ERROR_SUCCESS)
1345 return ERROR_SUCCESS;
1347 MSI_IterateRecords( view, NULL, load_media, package );
1348 msiobj_release( &view->hdr );
1349 return ERROR_SUCCESS;
1352 static UINT load_patch(MSIRECORD *row, LPVOID param)
1354 MSIPACKAGE *package = param;
1355 MSIFILEPATCH *patch;
1356 LPWSTR file_key;
1358 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1359 if (!patch)
1360 return ERROR_NOT_ENOUGH_MEMORY;
1362 file_key = msi_dup_record_field( row, 1 );
1363 patch->File = msi_get_loaded_file( package, file_key );
1364 msi_free(file_key);
1366 if( !patch->File )
1368 ERR("Failed to find target for patch in File table\n");
1369 msi_free(patch);
1370 return ERROR_FUNCTION_FAILED;
1373 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1375 /* FIXME: The database should be properly transformed */
1376 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1378 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1379 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1380 patch->IsApplied = FALSE;
1382 /* FIXME:
1383 * Header field - for patch validation.
1384 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1387 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1389 list_add_tail( &package->filepatches, &patch->entry );
1391 return ERROR_SUCCESS;
1394 static UINT load_all_patches(MSIPACKAGE *package)
1396 static const WCHAR query[] = {
1397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1398 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1399 '`','S','e','q','u','e','n','c','e','`',0};
1400 MSIQUERY *view;
1401 UINT rc;
1403 if (!list_empty(&package->filepatches))
1404 return ERROR_SUCCESS;
1406 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1407 if (rc != ERROR_SUCCESS)
1408 return ERROR_SUCCESS;
1410 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1411 msiobj_release(&view->hdr);
1412 return ERROR_SUCCESS;
1415 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1417 static const WCHAR query[] = {
1418 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1419 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1420 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1421 MSIQUERY *view;
1423 folder->persistent = FALSE;
1424 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1426 if (!MSI_ViewExecute( view, NULL ))
1428 MSIRECORD *rec;
1429 if (!MSI_ViewFetch( view, &rec ))
1431 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1432 folder->persistent = TRUE;
1433 msiobj_release( &rec->hdr );
1436 msiobj_release( &view->hdr );
1438 return ERROR_SUCCESS;
1441 static UINT load_folder( MSIRECORD *row, LPVOID param )
1443 MSIPACKAGE *package = param;
1444 static WCHAR szEmpty[] = { 0 };
1445 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1446 MSIFOLDER *folder;
1448 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1449 list_init( &folder->children );
1450 folder->Directory = msi_dup_record_field( row, 1 );
1451 folder->Parent = msi_dup_record_field( row, 2 );
1452 p = msi_dup_record_field(row, 3);
1454 TRACE("%s\n", debugstr_w(folder->Directory));
1456 /* split src and target dir */
1457 tgt_short = p;
1458 src_short = folder_split_path( p, ':' );
1460 /* split the long and short paths */
1461 tgt_long = folder_split_path( tgt_short, '|' );
1462 src_long = folder_split_path( src_short, '|' );
1464 /* check for no-op dirs */
1465 if (tgt_short && !strcmpW( szDot, tgt_short ))
1466 tgt_short = szEmpty;
1467 if (src_short && !strcmpW( szDot, src_short ))
1468 src_short = szEmpty;
1470 if (!tgt_long)
1471 tgt_long = tgt_short;
1473 if (!src_short) {
1474 src_short = tgt_short;
1475 src_long = tgt_long;
1478 if (!src_long)
1479 src_long = src_short;
1481 /* FIXME: use the target short path too */
1482 folder->TargetDefault = strdupW(tgt_long);
1483 folder->SourceShortPath = strdupW(src_short);
1484 folder->SourceLongPath = strdupW(src_long);
1485 msi_free(p);
1487 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1488 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1489 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1491 load_folder_persistence( package, folder );
1493 list_add_tail( &package->folders, &folder->entry );
1494 return ERROR_SUCCESS;
1497 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1499 FolderList *fl;
1501 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1502 fl->folder = child;
1503 list_add_tail( &parent->children, &fl->entry );
1504 return ERROR_SUCCESS;
1507 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1509 MSIPACKAGE *package = param;
1510 MSIFOLDER *parent, *child;
1512 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1513 return ERROR_FUNCTION_FAILED;
1515 if (!child->Parent) return ERROR_SUCCESS;
1517 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1518 return ERROR_FUNCTION_FAILED;
1520 return add_folder_child( parent, child );
1523 static UINT load_all_folders( MSIPACKAGE *package )
1525 static const WCHAR query[] = {
1526 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1527 '`','D','i','r','e','c','t','o','r','y','`',0};
1528 MSIQUERY *view;
1529 UINT r;
1531 if (!list_empty(&package->folders))
1532 return ERROR_SUCCESS;
1534 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1535 if (r != ERROR_SUCCESS)
1536 return r;
1538 r = MSI_IterateRecords( view, NULL, load_folder, package );
1539 if (r != ERROR_SUCCESS)
1541 msiobj_release( &view->hdr );
1542 return r;
1544 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1545 msiobj_release( &view->hdr );
1546 return r;
1549 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1551 msi_set_property( package->db, szCostingComplete, szZero );
1552 msi_set_property( package->db, szRootDrive, szCRoot );
1554 load_all_folders( package );
1555 msi_load_all_components( package );
1556 msi_load_all_features( package );
1557 load_all_files( package );
1558 load_all_patches( package );
1559 load_all_media( package );
1561 return ERROR_SUCCESS;
1564 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1566 const WCHAR *action = package->script->Actions[script][index];
1567 ui_actionstart( package, action );
1568 TRACE("executing %s\n", debugstr_w(action));
1569 return ACTION_PerformAction( package, action, script );
1572 static UINT execute_script( MSIPACKAGE *package, UINT script )
1574 UINT i, rc = ERROR_SUCCESS;
1576 TRACE("executing script %u\n", script);
1578 if (!package->script)
1580 ERR("no script!\n");
1581 return ERROR_FUNCTION_FAILED;
1583 if (script == ROLLBACK_SCRIPT)
1585 for (i = package->script->ActionCount[script]; i > 0; i--)
1587 rc = execute_script_action( package, script, i - 1 );
1588 if (rc != ERROR_SUCCESS) break;
1591 else
1593 for (i = 0; i < package->script->ActionCount[script]; i++)
1595 rc = execute_script_action( package, script, i );
1596 if (rc != ERROR_SUCCESS) break;
1599 msi_free_action_script(package, script);
1600 return rc;
1603 static UINT ACTION_FileCost(MSIPACKAGE *package)
1605 return ERROR_SUCCESS;
1608 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1610 MSICOMPONENT *comp;
1611 UINT r;
1613 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1615 if (!comp->ComponentId) continue;
1617 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1618 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1619 &comp->Installed );
1620 if (r != ERROR_SUCCESS)
1621 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1622 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1623 &comp->Installed );
1624 if (r != ERROR_SUCCESS)
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1627 &comp->Installed );
1628 if (r != ERROR_SUCCESS)
1629 comp->Installed = INSTALLSTATE_ABSENT;
1633 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1635 MSIFEATURE *feature;
1637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1639 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1641 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1642 feature->Installed = INSTALLSTATE_ABSENT;
1643 else
1644 feature->Installed = state;
1648 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1650 return (feature->Level > 0 && feature->Level <= level);
1653 static BOOL process_state_property(MSIPACKAGE* package, int level,
1654 LPCWSTR property, INSTALLSTATE state)
1656 LPWSTR override;
1657 MSIFEATURE *feature;
1659 override = msi_dup_property( package->db, property );
1660 if (!override)
1661 return FALSE;
1663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1665 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1666 continue;
1668 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1670 if (!strcmpiW( override, szAll ))
1672 if (feature->Installed != state)
1674 feature->Action = state;
1675 feature->ActionRequest = state;
1678 else
1680 LPWSTR ptr = override;
1681 LPWSTR ptr2 = strchrW(override,',');
1683 while (ptr)
1685 int len = ptr2 - ptr;
1687 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1688 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1690 if (feature->Installed != state)
1692 feature->Action = state;
1693 feature->ActionRequest = state;
1695 break;
1697 if (ptr2)
1699 ptr=ptr2+1;
1700 ptr2 = strchrW(ptr,',');
1702 else
1703 break;
1707 msi_free(override);
1708 return TRUE;
1711 static BOOL process_overrides( MSIPACKAGE *package, int level )
1713 static const WCHAR szAddLocal[] =
1714 {'A','D','D','L','O','C','A','L',0};
1715 static const WCHAR szAddSource[] =
1716 {'A','D','D','S','O','U','R','C','E',0};
1717 static const WCHAR szAdvertise[] =
1718 {'A','D','V','E','R','T','I','S','E',0};
1719 BOOL ret = FALSE;
1721 /* all these activation/deactivation things happen in order and things
1722 * later on the list override things earlier on the list.
1724 * 0 INSTALLLEVEL processing
1725 * 1 ADDLOCAL
1726 * 2 REMOVE
1727 * 3 ADDSOURCE
1728 * 4 ADDDEFAULT
1729 * 5 REINSTALL
1730 * 6 ADVERTISE
1731 * 7 COMPADDLOCAL
1732 * 8 COMPADDSOURCE
1733 * 9 FILEADDLOCAL
1734 * 10 FILEADDSOURCE
1735 * 11 FILEADDDEFAULT
1737 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1738 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1739 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1740 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1741 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1743 if (ret)
1744 msi_set_property( package->db, szPreselected, szOne );
1746 return ret;
1749 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1751 int level;
1752 MSICOMPONENT* component;
1753 MSIFEATURE *feature;
1755 TRACE("Checking Install Level\n");
1757 level = msi_get_property_int(package->db, szInstallLevel, 1);
1759 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1761 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1763 if (!is_feature_selected( feature, level )) continue;
1765 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1767 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1769 feature->Action = INSTALLSTATE_SOURCE;
1770 feature->ActionRequest = INSTALLSTATE_SOURCE;
1772 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1774 feature->Action = INSTALLSTATE_ADVERTISED;
1775 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1777 else
1779 feature->Action = INSTALLSTATE_LOCAL;
1780 feature->ActionRequest = INSTALLSTATE_LOCAL;
1784 /* disable child features of unselected parent or follow parent */
1785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1787 FeatureList *fl;
1789 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1791 if (!is_feature_selected( feature, level ))
1793 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1794 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1796 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1798 fl->feature->Action = feature->Action;
1799 fl->feature->ActionRequest = feature->ActionRequest;
1804 else /* preselected */
1806 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1808 if (!is_feature_selected( feature, level )) continue;
1810 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1812 if (feature->Installed == INSTALLSTATE_ABSENT)
1814 feature->Action = INSTALLSTATE_UNKNOWN;
1815 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1817 else
1819 feature->Action = feature->Installed;
1820 feature->ActionRequest = feature->Installed;
1824 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1826 FeatureList *fl;
1828 if (!is_feature_selected( feature, level )) continue;
1830 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1832 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1834 fl->feature->Action = feature->Action;
1835 fl->feature->ActionRequest = feature->ActionRequest;
1841 /* now we want to set component state based based on feature state */
1842 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 ComponentList *cl;
1846 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1847 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1848 feature->ActionRequest, feature->Action);
1850 if (!is_feature_selected( feature, level )) continue;
1852 /* features with components that have compressed files are made local */
1853 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1855 if (cl->component->ForceLocalState &&
1856 feature->ActionRequest == INSTALLSTATE_SOURCE)
1858 feature->Action = INSTALLSTATE_LOCAL;
1859 feature->ActionRequest = INSTALLSTATE_LOCAL;
1860 break;
1864 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1866 component = cl->component;
1868 switch (feature->ActionRequest)
1870 case INSTALLSTATE_ABSENT:
1871 component->anyAbsent = 1;
1872 break;
1873 case INSTALLSTATE_ADVERTISED:
1874 component->hasAdvertiseFeature = 1;
1875 break;
1876 case INSTALLSTATE_SOURCE:
1877 component->hasSourceFeature = 1;
1878 break;
1879 case INSTALLSTATE_LOCAL:
1880 component->hasLocalFeature = 1;
1881 break;
1882 case INSTALLSTATE_DEFAULT:
1883 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1884 component->hasAdvertiseFeature = 1;
1885 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1886 component->hasSourceFeature = 1;
1887 else
1888 component->hasLocalFeature = 1;
1889 break;
1890 default:
1891 break;
1896 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1898 /* check if it's local or source */
1899 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1900 (component->hasLocalFeature || component->hasSourceFeature))
1902 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1903 !component->ForceLocalState)
1905 component->Action = INSTALLSTATE_SOURCE;
1906 component->ActionRequest = INSTALLSTATE_SOURCE;
1908 else
1910 component->Action = INSTALLSTATE_LOCAL;
1911 component->ActionRequest = INSTALLSTATE_LOCAL;
1913 continue;
1916 /* if any feature is local, the component must be local too */
1917 if (component->hasLocalFeature)
1919 component->Action = INSTALLSTATE_LOCAL;
1920 component->ActionRequest = INSTALLSTATE_LOCAL;
1921 continue;
1923 if (component->hasSourceFeature)
1925 component->Action = INSTALLSTATE_SOURCE;
1926 component->ActionRequest = INSTALLSTATE_SOURCE;
1927 continue;
1929 if (component->hasAdvertiseFeature)
1931 component->Action = INSTALLSTATE_ADVERTISED;
1932 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1933 continue;
1935 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1936 if (component->anyAbsent &&
1937 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1939 component->Action = INSTALLSTATE_ABSENT;
1940 component->ActionRequest = INSTALLSTATE_ABSENT;
1944 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1946 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1948 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1949 component->Action = INSTALLSTATE_LOCAL;
1950 component->ActionRequest = INSTALLSTATE_LOCAL;
1953 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1954 component->Installed == INSTALLSTATE_SOURCE &&
1955 component->hasSourceFeature)
1957 component->Action = INSTALLSTATE_UNKNOWN;
1958 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1961 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1962 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1965 return ERROR_SUCCESS;
1968 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1970 MSIPACKAGE *package = param;
1971 LPCWSTR name;
1972 MSIFEATURE *feature;
1974 name = MSI_RecordGetString( row, 1 );
1976 feature = msi_get_loaded_feature( package, name );
1977 if (!feature)
1978 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1979 else
1981 LPCWSTR Condition;
1982 Condition = MSI_RecordGetString(row,3);
1984 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1986 int level = MSI_RecordGetInteger(row,2);
1987 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1988 feature->Level = level;
1991 return ERROR_SUCCESS;
1994 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1996 static const WCHAR name[] = {'\\',0};
1997 VS_FIXEDFILEINFO *ptr, *ret;
1998 LPVOID version;
1999 DWORD versize, handle;
2000 UINT sz;
2002 versize = GetFileVersionInfoSizeW( filename, &handle );
2003 if (!versize)
2004 return NULL;
2006 version = msi_alloc( versize );
2007 if (!version)
2008 return NULL;
2010 GetFileVersionInfoW( filename, 0, versize, version );
2012 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2014 msi_free( version );
2015 return NULL;
2018 ret = msi_alloc( sz );
2019 memcpy( ret, ptr, sz );
2021 msi_free( version );
2022 return ret;
2025 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2027 DWORD ms, ls;
2029 msi_parse_version_string( version, &ms, &ls );
2031 if (fi->dwFileVersionMS > ms) return 1;
2032 else if (fi->dwFileVersionMS < ms) return -1;
2033 else if (fi->dwFileVersionLS > ls) return 1;
2034 else if (fi->dwFileVersionLS < ls) return -1;
2035 return 0;
2038 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2040 DWORD ms1, ms2;
2042 msi_parse_version_string( ver1, &ms1, NULL );
2043 msi_parse_version_string( ver2, &ms2, NULL );
2045 if (ms1 > ms2) return 1;
2046 else if (ms1 < ms2) return -1;
2047 return 0;
2050 DWORD msi_get_disk_file_size( LPCWSTR filename )
2052 HANDLE file;
2053 DWORD size;
2055 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2056 if (file == INVALID_HANDLE_VALUE)
2057 return INVALID_FILE_SIZE;
2059 size = GetFileSize( file, NULL );
2060 TRACE("size is %u\n", size);
2061 CloseHandle( file );
2062 return size;
2065 BOOL msi_file_hash_matches( MSIFILE *file )
2067 UINT r;
2068 MSIFILEHASHINFO hash;
2070 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2071 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2072 if (r != ERROR_SUCCESS)
2073 return FALSE;
2075 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2078 static WCHAR *get_temp_dir( void )
2080 static UINT id;
2081 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2083 GetTempPathW( MAX_PATH, tmp );
2084 for (;;)
2086 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2087 if (CreateDirectoryW( dir, NULL )) break;
2089 return strdupW( dir );
2093 * msi_build_directory_name()
2095 * This function is to save messing round with directory names
2096 * It handles adding backslashes between path segments,
2097 * and can add \ at the end of the directory name if told to.
2099 * It takes a variable number of arguments.
2100 * It always allocates a new string for the result, so make sure
2101 * to free the return value when finished with it.
2103 * The first arg is the number of path segments that follow.
2104 * The arguments following count are a list of path segments.
2105 * A path segment may be NULL.
2107 * Path segments will be added with a \ separating them.
2108 * A \ will not be added after the last segment, however if the
2109 * last segment is NULL, then the last character will be a \
2111 WCHAR *msi_build_directory_name( DWORD count, ... )
2113 DWORD sz = 1, i;
2114 WCHAR *dir;
2115 va_list va;
2117 va_start( va, count );
2118 for (i = 0; i < count; i++)
2120 const WCHAR *str = va_arg( va, const WCHAR * );
2121 if (str) sz += strlenW( str ) + 1;
2123 va_end( va );
2125 dir = msi_alloc( sz * sizeof(WCHAR) );
2126 dir[0] = 0;
2128 va_start( va, count );
2129 for (i = 0; i < count; i++)
2131 const WCHAR *str = va_arg( va, const WCHAR * );
2132 if (!str) continue;
2133 strcatW( dir, str );
2134 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2136 va_end( va );
2137 return dir;
2140 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2142 MSIASSEMBLY *assembly = file->Component->assembly;
2144 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2146 msi_free( file->TargetPath );
2147 if (assembly && !assembly->application)
2149 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2150 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2151 msi_track_tempfile( package, file->TargetPath );
2153 else
2155 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2156 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2159 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2162 static UINT calculate_file_cost( MSIPACKAGE *package )
2164 VS_FIXEDFILEINFO *file_version;
2165 WCHAR *font_version;
2166 MSIFILE *file;
2168 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2170 MSICOMPONENT *comp = file->Component;
2171 DWORD file_size;
2173 if (!comp->Enabled) continue;
2175 if (file->IsCompressed)
2176 comp->ForceLocalState = TRUE;
2178 set_target_path( package, file );
2180 if ((comp->assembly && !comp->assembly->installed) ||
2181 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2183 comp->Cost += file->FileSize;
2184 continue;
2186 file_size = msi_get_disk_file_size( file->TargetPath );
2188 if (file->Version)
2190 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2192 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2194 comp->Cost += file->FileSize - file_size;
2196 msi_free( file_version );
2197 continue;
2199 else if ((font_version = font_version_from_file( file->TargetPath )))
2201 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2203 comp->Cost += file->FileSize - file_size;
2205 msi_free( font_version );
2206 continue;
2209 if (file_size != file->FileSize)
2211 comp->Cost += file->FileSize - file_size;
2214 return ERROR_SUCCESS;
2217 void msi_clean_path( WCHAR *p )
2219 WCHAR *q = p;
2220 int n, len = 0;
2222 while (1)
2224 /* copy until the end of the string or a space */
2225 while (*p != ' ' && (*q = *p))
2227 p++, len++;
2228 /* reduce many backslashes to one */
2229 if (*p != '\\' || *q != '\\')
2230 q++;
2233 /* quit at the end of the string */
2234 if (!*p)
2235 break;
2237 /* count the number of spaces */
2238 n = 0;
2239 while (p[n] == ' ')
2240 n++;
2242 /* if it's leading or trailing space, skip it */
2243 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2244 p += n;
2245 else /* copy n spaces */
2246 while (n && (*q++ = *p++)) n--;
2250 static WCHAR *get_target_dir_property( MSIDATABASE *db )
2252 int len;
2253 WCHAR *path, *target_dir = msi_dup_property( db, szTargetDir );
2255 if (!target_dir) return NULL;
2257 len = strlenW( target_dir );
2258 if (target_dir[len - 1] == '\\') return target_dir;
2259 if ((path = msi_alloc( (len + 2) * sizeof(WCHAR) )))
2261 strcpyW( path, target_dir );
2262 path[len] = '\\';
2263 path[len + 1] = 0;
2265 msi_free( target_dir );
2266 return path;
2269 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2271 FolderList *fl;
2272 MSIFOLDER *folder, *parent, *child;
2273 WCHAR *path;
2275 TRACE("resolving %s\n", debugstr_w(name));
2277 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2279 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2281 if (!load_prop || !(path = get_target_dir_property( package->db )))
2283 path = msi_dup_property( package->db, szRootDrive );
2286 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2288 parent = msi_get_loaded_folder( package, folder->Parent );
2289 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2291 msi_clean_path( path );
2292 if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2294 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2295 msi_free( path );
2296 return;
2298 msi_set_property( package->db, folder->Directory, path );
2299 msi_free( folder->ResolvedTarget );
2300 folder->ResolvedTarget = path;
2302 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2304 child = fl->folder;
2305 msi_resolve_target_folder( package, child->Directory, load_prop );
2307 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2310 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2312 static const WCHAR query[] = {
2313 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2314 '`','C','o','n','d','i','t','i','o','n','`',0};
2315 static const WCHAR szOutOfDiskSpace[] = {
2316 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2317 MSICOMPONENT *comp;
2318 MSIQUERY *view;
2319 LPWSTR level;
2320 UINT rc;
2322 TRACE("Building directory properties\n");
2323 msi_resolve_target_folder( package, szTargetDir, TRUE );
2325 TRACE("Evaluating component conditions\n");
2326 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2328 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2330 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2331 comp->Enabled = FALSE;
2333 else
2334 comp->Enabled = TRUE;
2337 /* read components states from the registry */
2338 ACTION_GetComponentInstallStates(package);
2339 ACTION_GetFeatureInstallStates(package);
2341 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2343 TRACE("Evaluating feature conditions\n");
2345 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2346 if (rc == ERROR_SUCCESS)
2348 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2349 msiobj_release( &view->hdr );
2353 TRACE("Calculating file cost\n");
2354 calculate_file_cost( package );
2356 msi_set_property( package->db, szCostingComplete, szOne );
2357 /* set default run level if not set */
2358 level = msi_dup_property( package->db, szInstallLevel );
2359 if (!level)
2360 msi_set_property( package->db, szInstallLevel, szOne );
2361 msi_free(level);
2363 /* FIXME: check volume disk space */
2364 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2366 return MSI_SetFeatureStates(package);
2369 /* OK this value is "interpreted" and then formatted based on the
2370 first few characters */
2371 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2372 DWORD *size)
2374 LPSTR data = NULL;
2376 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2378 if (value[1]=='x')
2380 LPWSTR ptr;
2381 CHAR byte[5];
2382 LPWSTR deformated = NULL;
2383 int count;
2385 deformat_string(package, &value[2], &deformated);
2387 /* binary value type */
2388 ptr = deformated;
2389 *type = REG_BINARY;
2390 if (strlenW(ptr)%2)
2391 *size = (strlenW(ptr)/2)+1;
2392 else
2393 *size = strlenW(ptr)/2;
2395 data = msi_alloc(*size);
2397 byte[0] = '0';
2398 byte[1] = 'x';
2399 byte[4] = 0;
2400 count = 0;
2401 /* if uneven pad with a zero in front */
2402 if (strlenW(ptr)%2)
2404 byte[2]= '0';
2405 byte[3]= *ptr;
2406 ptr++;
2407 data[count] = (BYTE)strtol(byte,NULL,0);
2408 count ++;
2409 TRACE("Uneven byte count\n");
2411 while (*ptr)
2413 byte[2]= *ptr;
2414 ptr++;
2415 byte[3]= *ptr;
2416 ptr++;
2417 data[count] = (BYTE)strtol(byte,NULL,0);
2418 count ++;
2420 msi_free(deformated);
2422 TRACE("Data %i bytes(%i)\n",*size,count);
2424 else
2426 LPWSTR deformated;
2427 LPWSTR p;
2428 DWORD d = 0;
2429 deformat_string(package, &value[1], &deformated);
2431 *type=REG_DWORD;
2432 *size = sizeof(DWORD);
2433 data = msi_alloc(*size);
2434 p = deformated;
2435 if (*p == '-')
2436 p++;
2437 while (*p)
2439 if ( (*p < '0') || (*p > '9') )
2440 break;
2441 d *= 10;
2442 d += (*p - '0');
2443 p++;
2445 if (deformated[0] == '-')
2446 d = -d;
2447 *(LPDWORD)data = d;
2448 TRACE("DWORD %i\n",*(LPDWORD)data);
2450 msi_free(deformated);
2453 else
2455 static const WCHAR szMulti[] = {'[','~',']',0};
2456 LPCWSTR ptr;
2457 *type=REG_SZ;
2459 if (value[0]=='#')
2461 if (value[1]=='%')
2463 ptr = &value[2];
2464 *type=REG_EXPAND_SZ;
2466 else
2467 ptr = &value[1];
2469 else
2470 ptr=value;
2472 if (strstrW(value, szMulti))
2473 *type = REG_MULTI_SZ;
2475 /* remove initial delimiter */
2476 if (!strncmpW(value, szMulti, 3))
2477 ptr = value + 3;
2479 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2481 /* add double NULL terminator */
2482 if (*type == REG_MULTI_SZ)
2484 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2485 data = msi_realloc_zero(data, *size);
2488 return data;
2491 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2493 const WCHAR *ret;
2495 switch (root)
2497 case -1:
2498 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2500 *root_key = HKEY_LOCAL_MACHINE;
2501 ret = szHLM;
2503 else
2505 *root_key = HKEY_CURRENT_USER;
2506 ret = szHCU;
2508 break;
2509 case 0:
2510 *root_key = HKEY_CLASSES_ROOT;
2511 ret = szHCR;
2512 break;
2513 case 1:
2514 *root_key = HKEY_CURRENT_USER;
2515 ret = szHCU;
2516 break;
2517 case 2:
2518 *root_key = HKEY_LOCAL_MACHINE;
2519 ret = szHLM;
2520 break;
2521 case 3:
2522 *root_key = HKEY_USERS;
2523 ret = szHU;
2524 break;
2525 default:
2526 ERR("Unknown root %i\n", root);
2527 return NULL;
2530 return ret;
2533 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2535 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2536 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2538 if (is_64bit && package->platform == PLATFORM_INTEL &&
2539 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2541 UINT size;
2542 WCHAR *path_32node;
2544 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2545 if (!(path_32node = msi_alloc( size ))) return NULL;
2547 memcpy( path_32node, path, len * sizeof(WCHAR) );
2548 strcpyW( path_32node + len, szWow6432Node );
2549 strcatW( path_32node, szBackSlash );
2550 strcatW( path_32node, path + len );
2551 return path_32node;
2554 return strdupW( path );
2557 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2559 MSIPACKAGE *package = param;
2560 LPSTR value_data = NULL;
2561 HKEY root_key, hkey;
2562 DWORD type,size;
2563 LPWSTR deformated, uikey, keypath;
2564 LPCWSTR szRoot, component, name, key, value;
2565 MSICOMPONENT *comp;
2566 MSIRECORD * uirow;
2567 INT root;
2568 BOOL check_first = FALSE;
2569 UINT rc;
2571 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2573 component = MSI_RecordGetString(row, 6);
2574 comp = msi_get_loaded_component(package,component);
2575 if (!comp)
2576 return ERROR_SUCCESS;
2578 comp->Action = msi_get_component_action( package, comp );
2579 if (comp->Action != INSTALLSTATE_LOCAL)
2581 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2582 return ERROR_SUCCESS;
2585 name = MSI_RecordGetString(row, 4);
2586 if( MSI_RecordIsNull(row,5) && name )
2588 /* null values can have special meanings */
2589 if (name[0]=='-' && name[1] == 0)
2590 return ERROR_SUCCESS;
2591 else if ((name[0]=='+' && name[1] == 0) ||
2592 (name[0] == '*' && name[1] == 0))
2593 name = NULL;
2594 check_first = TRUE;
2597 root = MSI_RecordGetInteger(row,2);
2598 key = MSI_RecordGetString(row, 3);
2600 szRoot = get_root_key( package, root, &root_key );
2601 if (!szRoot)
2602 return ERROR_SUCCESS;
2604 deformat_string(package, key , &deformated);
2605 size = strlenW(deformated) + strlenW(szRoot) + 1;
2606 uikey = msi_alloc(size*sizeof(WCHAR));
2607 strcpyW(uikey,szRoot);
2608 strcatW(uikey,deformated);
2610 keypath = get_keypath( package, root_key, deformated );
2611 msi_free( deformated );
2612 if (RegCreateKeyW( root_key, keypath, &hkey ))
2614 ERR("Could not create key %s\n", debugstr_w(keypath));
2615 msi_free(uikey);
2616 msi_free(keypath);
2617 return ERROR_SUCCESS;
2620 value = MSI_RecordGetString(row,5);
2621 if (value)
2622 value_data = parse_value(package, value, &type, &size);
2623 else
2625 value_data = (LPSTR)strdupW(szEmpty);
2626 size = sizeof(szEmpty);
2627 type = REG_SZ;
2630 deformat_string(package, name, &deformated);
2632 if (!check_first)
2634 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2635 debugstr_w(uikey));
2636 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2638 else
2640 DWORD sz = 0;
2641 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2642 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2644 TRACE("value %s of %s checked already exists\n",
2645 debugstr_w(deformated), debugstr_w(uikey));
2647 else
2649 TRACE("Checked and setting value %s of %s\n",
2650 debugstr_w(deformated), debugstr_w(uikey));
2651 if (deformated || size)
2652 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2655 RegCloseKey(hkey);
2657 uirow = MSI_CreateRecord(3);
2658 MSI_RecordSetStringW(uirow,2,deformated);
2659 MSI_RecordSetStringW(uirow,1,uikey);
2660 if (type == REG_SZ || type == REG_EXPAND_SZ)
2661 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2662 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2663 msiobj_release( &uirow->hdr );
2665 msi_free(value_data);
2666 msi_free(deformated);
2667 msi_free(uikey);
2668 msi_free(keypath);
2670 return ERROR_SUCCESS;
2673 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2675 static const WCHAR query[] = {
2676 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2677 '`','R','e','g','i','s','t','r','y','`',0};
2678 MSIQUERY *view;
2679 UINT rc;
2681 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2682 if (rc != ERROR_SUCCESS)
2683 return ERROR_SUCCESS;
2685 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2686 msiobj_release(&view->hdr);
2687 return rc;
2690 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2692 LONG res;
2693 HKEY hkey;
2694 DWORD num_subkeys, num_values;
2696 if (delete_key)
2698 if ((res = RegDeleteTreeW( hkey_root, key )))
2700 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2702 return;
2705 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2707 if ((res = RegDeleteValueW( hkey, value )))
2709 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2711 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2712 NULL, NULL, NULL, NULL );
2713 RegCloseKey( hkey );
2714 if (!res && !num_subkeys && !num_values)
2716 TRACE("Removing empty key %s\n", debugstr_w(key));
2717 RegDeleteKeyW( hkey_root, key );
2719 return;
2721 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2725 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2727 MSIPACKAGE *package = param;
2728 LPCWSTR component, name, key_str, root_key_str;
2729 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2730 MSICOMPONENT *comp;
2731 MSIRECORD *uirow;
2732 BOOL delete_key = FALSE;
2733 HKEY hkey_root;
2734 UINT size;
2735 INT root;
2737 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2739 component = MSI_RecordGetString( row, 6 );
2740 comp = msi_get_loaded_component( package, component );
2741 if (!comp)
2742 return ERROR_SUCCESS;
2744 comp->Action = msi_get_component_action( package, comp );
2745 if (comp->Action != INSTALLSTATE_ABSENT)
2747 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2748 return ERROR_SUCCESS;
2751 name = MSI_RecordGetString( row, 4 );
2752 if (MSI_RecordIsNull( row, 5 ) && name )
2754 if (name[0] == '+' && !name[1])
2755 return ERROR_SUCCESS;
2756 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2758 delete_key = TRUE;
2759 name = NULL;
2763 root = MSI_RecordGetInteger( row, 2 );
2764 key_str = MSI_RecordGetString( row, 3 );
2766 root_key_str = get_root_key( package, root, &hkey_root );
2767 if (!root_key_str)
2768 return ERROR_SUCCESS;
2770 deformat_string( package, key_str, &deformated_key );
2771 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2772 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2773 strcpyW( ui_key_str, root_key_str );
2774 strcatW( ui_key_str, deformated_key );
2776 deformat_string( package, name, &deformated_name );
2778 keypath = get_keypath( package, hkey_root, deformated_key );
2779 msi_free( deformated_key );
2780 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2781 msi_free( keypath );
2783 uirow = MSI_CreateRecord( 2 );
2784 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2785 MSI_RecordSetStringW( uirow, 2, deformated_name );
2786 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2787 msiobj_release( &uirow->hdr );
2789 msi_free( ui_key_str );
2790 msi_free( deformated_name );
2791 return ERROR_SUCCESS;
2794 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2796 MSIPACKAGE *package = param;
2797 LPCWSTR component, name, key_str, root_key_str;
2798 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2799 MSICOMPONENT *comp;
2800 MSIRECORD *uirow;
2801 BOOL delete_key = FALSE;
2802 HKEY hkey_root;
2803 UINT size;
2804 INT root;
2806 component = MSI_RecordGetString( row, 5 );
2807 comp = msi_get_loaded_component( package, component );
2808 if (!comp)
2809 return ERROR_SUCCESS;
2811 comp->Action = msi_get_component_action( package, comp );
2812 if (comp->Action != INSTALLSTATE_LOCAL)
2814 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2815 return ERROR_SUCCESS;
2818 if ((name = MSI_RecordGetString( row, 4 )))
2820 if (name[0] == '-' && !name[1])
2822 delete_key = TRUE;
2823 name = NULL;
2827 root = MSI_RecordGetInteger( row, 2 );
2828 key_str = MSI_RecordGetString( row, 3 );
2830 root_key_str = get_root_key( package, root, &hkey_root );
2831 if (!root_key_str)
2832 return ERROR_SUCCESS;
2834 deformat_string( package, key_str, &deformated_key );
2835 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2836 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2837 strcpyW( ui_key_str, root_key_str );
2838 strcatW( ui_key_str, deformated_key );
2840 deformat_string( package, name, &deformated_name );
2842 keypath = get_keypath( package, hkey_root, deformated_key );
2843 msi_free( deformated_key );
2844 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2845 msi_free( keypath );
2847 uirow = MSI_CreateRecord( 2 );
2848 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2849 MSI_RecordSetStringW( uirow, 2, deformated_name );
2850 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2851 msiobj_release( &uirow->hdr );
2853 msi_free( ui_key_str );
2854 msi_free( deformated_name );
2855 return ERROR_SUCCESS;
2858 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2860 static const WCHAR registry_query[] = {
2861 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2862 '`','R','e','g','i','s','t','r','y','`',0};
2863 static const WCHAR remove_registry_query[] = {
2864 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2865 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2866 MSIQUERY *view;
2867 UINT rc;
2869 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2870 if (rc == ERROR_SUCCESS)
2872 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2873 msiobj_release( &view->hdr );
2874 if (rc != ERROR_SUCCESS)
2875 return rc;
2877 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2878 if (rc == ERROR_SUCCESS)
2880 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2881 msiobj_release( &view->hdr );
2882 if (rc != ERROR_SUCCESS)
2883 return rc;
2885 return ERROR_SUCCESS;
2888 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2890 package->script->CurrentlyScripting = TRUE;
2892 return ERROR_SUCCESS;
2896 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2898 static const WCHAR query[]= {
2899 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2900 '`','R','e','g','i','s','t','r','y','`',0};
2901 MSICOMPONENT *comp;
2902 DWORD total = 0, count = 0;
2903 MSIQUERY *view;
2904 MSIFEATURE *feature;
2905 MSIFILE *file;
2906 UINT rc;
2908 TRACE("InstallValidate\n");
2910 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2911 if (rc == ERROR_SUCCESS)
2913 MSI_IterateRecords( view, &count, NULL, package );
2914 msiobj_release( &view->hdr );
2915 total += count * REG_PROGRESS_VALUE;
2917 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2918 total += COMPONENT_PROGRESS_VALUE;
2920 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2921 total += file->FileSize;
2923 msi_ui_progress( package, 0, total, 0, 0 );
2925 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2927 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2928 debugstr_w(feature->Feature), feature->Installed,
2929 feature->ActionRequest, feature->Action);
2931 return ERROR_SUCCESS;
2934 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2936 MSIPACKAGE* package = param;
2937 LPCWSTR cond = NULL;
2938 LPCWSTR message = NULL;
2939 UINT r;
2941 static const WCHAR title[]=
2942 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2944 cond = MSI_RecordGetString(row,1);
2946 r = MSI_EvaluateConditionW(package,cond);
2947 if (r == MSICONDITION_FALSE)
2949 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2951 LPWSTR deformated;
2952 message = MSI_RecordGetString(row,2);
2953 deformat_string(package,message,&deformated);
2954 MessageBoxW(NULL,deformated,title,MB_OK);
2955 msi_free(deformated);
2958 return ERROR_INSTALL_FAILURE;
2961 return ERROR_SUCCESS;
2964 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2966 static const WCHAR query[] = {
2967 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2968 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2969 MSIQUERY *view;
2970 UINT rc;
2972 TRACE("Checking launch conditions\n");
2974 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2975 if (rc != ERROR_SUCCESS)
2976 return ERROR_SUCCESS;
2978 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2979 msiobj_release(&view->hdr);
2980 return rc;
2983 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2986 if (!cmp->KeyPath)
2987 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2989 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2991 static const WCHAR query[] = {
2992 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2993 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2994 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2995 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
2996 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2997 MSIRECORD *row;
2998 UINT root, len;
2999 LPWSTR deformated, buffer, deformated_name;
3000 LPCWSTR key, name;
3002 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3003 if (!row)
3004 return NULL;
3006 root = MSI_RecordGetInteger(row,2);
3007 key = MSI_RecordGetString(row, 3);
3008 name = MSI_RecordGetString(row, 4);
3009 deformat_string(package, key , &deformated);
3010 deformat_string(package, name, &deformated_name);
3012 len = strlenW(deformated) + 6;
3013 if (deformated_name)
3014 len+=strlenW(deformated_name);
3016 buffer = msi_alloc( len *sizeof(WCHAR));
3018 if (deformated_name)
3019 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3020 else
3021 sprintfW(buffer,fmt,root,deformated);
3023 msi_free(deformated);
3024 msi_free(deformated_name);
3025 msiobj_release(&row->hdr);
3027 return buffer;
3029 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3031 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3032 return NULL;
3034 else
3036 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3038 if (file)
3039 return strdupW( file->TargetPath );
3041 return NULL;
3044 static HKEY openSharedDLLsKey(void)
3046 HKEY hkey=0;
3047 static const WCHAR path[] =
3048 {'S','o','f','t','w','a','r','e','\\',
3049 'M','i','c','r','o','s','o','f','t','\\',
3050 'W','i','n','d','o','w','s','\\',
3051 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3052 'S','h','a','r','e','d','D','L','L','s',0};
3054 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3055 return hkey;
3058 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3060 HKEY hkey;
3061 DWORD count=0;
3062 DWORD type;
3063 DWORD sz = sizeof(count);
3064 DWORD rc;
3066 hkey = openSharedDLLsKey();
3067 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3068 if (rc != ERROR_SUCCESS)
3069 count = 0;
3070 RegCloseKey(hkey);
3071 return count;
3074 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3076 HKEY hkey;
3078 hkey = openSharedDLLsKey();
3079 if (count > 0)
3080 msi_reg_set_val_dword( hkey, path, count );
3081 else
3082 RegDeleteValueW(hkey,path);
3083 RegCloseKey(hkey);
3084 return count;
3087 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3089 MSIFEATURE *feature;
3090 INT count = 0;
3091 BOOL write = FALSE;
3093 /* only refcount DLLs */
3094 if (comp->KeyPath == NULL ||
3095 comp->assembly ||
3096 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3097 comp->Attributes & msidbComponentAttributesODBCDataSource)
3098 write = FALSE;
3099 else
3101 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3102 write = (count > 0);
3104 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3105 write = TRUE;
3108 /* increment counts */
3109 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3111 ComponentList *cl;
3113 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3114 continue;
3116 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3118 if ( cl->component == comp )
3119 count++;
3123 /* decrement counts */
3124 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3126 ComponentList *cl;
3128 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3129 continue;
3131 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3133 if ( cl->component == comp )
3134 count--;
3138 /* ref count all the files in the component */
3139 if (write)
3141 MSIFILE *file;
3143 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3145 if (file->Component == comp)
3146 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3150 /* add a count for permanent */
3151 if (comp->Attributes & msidbComponentAttributesPermanent)
3152 count ++;
3154 comp->RefCount = count;
3156 if (write)
3157 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3160 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3162 if (comp->assembly)
3164 const WCHAR prefixW[] = {'<','\\',0};
3165 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3166 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3168 if (keypath)
3170 strcpyW( keypath, prefixW );
3171 strcatW( keypath, comp->assembly->display_name );
3173 return keypath;
3175 return resolve_keypath( package, comp );
3178 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3180 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3181 UINT rc;
3182 MSICOMPONENT *comp;
3183 HKEY hkey;
3185 TRACE("\n");
3187 squash_guid(package->ProductCode,squished_pc);
3188 msi_set_sourcedir_props(package, FALSE);
3190 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3192 MSIRECORD *uirow;
3193 INSTALLSTATE action;
3195 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3196 if (!comp->ComponentId)
3197 continue;
3199 squash_guid( comp->ComponentId, squished_cc );
3200 msi_free( comp->FullKeypath );
3201 comp->FullKeypath = build_full_keypath( package, comp );
3203 ACTION_RefCountComponent( package, comp );
3205 if (package->need_rollback) action = comp->Installed;
3206 else action = comp->ActionRequest;
3208 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3209 debugstr_w(comp->Component), debugstr_w(squished_cc),
3210 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3212 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3214 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3215 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3216 else
3217 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3219 if (rc != ERROR_SUCCESS)
3220 continue;
3222 if (comp->Attributes & msidbComponentAttributesPermanent)
3224 static const WCHAR szPermKey[] =
3225 { '0','0','0','0','0','0','0','0','0','0','0','0',
3226 '0','0','0','0','0','0','0','0','0','0','0','0',
3227 '0','0','0','0','0','0','0','0',0 };
3229 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3231 if (action == INSTALLSTATE_LOCAL)
3232 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3233 else
3235 MSIFILE *file;
3236 MSIRECORD *row;
3237 LPWSTR ptr, ptr2;
3238 WCHAR source[MAX_PATH];
3239 WCHAR base[MAX_PATH];
3240 LPWSTR sourcepath;
3242 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3243 static const WCHAR query[] = {
3244 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3245 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3246 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3247 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3248 '`','D','i','s','k','I','d','`',0};
3250 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3251 continue;
3253 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3254 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3255 ptr2 = strrchrW(source, '\\') + 1;
3256 msiobj_release(&row->hdr);
3258 lstrcpyW(base, package->PackagePath);
3259 ptr = strrchrW(base, '\\');
3260 *(ptr + 1) = '\0';
3262 sourcepath = msi_resolve_file_source(package, file);
3263 ptr = sourcepath + lstrlenW(base);
3264 lstrcpyW(ptr2, ptr);
3265 msi_free(sourcepath);
3267 msi_reg_set_val_str(hkey, squished_pc, source);
3269 RegCloseKey(hkey);
3271 else if (action == INSTALLSTATE_ABSENT)
3273 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3274 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3275 else
3276 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3279 /* UI stuff */
3280 uirow = MSI_CreateRecord(3);
3281 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3282 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3283 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3284 msi_ui_actiondata( package, szProcessComponents, uirow );
3285 msiobj_release( &uirow->hdr );
3287 return ERROR_SUCCESS;
3290 typedef struct {
3291 CLSID clsid;
3292 LPWSTR source;
3294 LPWSTR path;
3295 ITypeLib *ptLib;
3296 } typelib_struct;
3298 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3299 LPWSTR lpszName, LONG_PTR lParam)
3301 TLIBATTR *attr;
3302 typelib_struct *tl_struct = (typelib_struct*) lParam;
3303 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3304 int sz;
3305 HRESULT res;
3307 if (!IS_INTRESOURCE(lpszName))
3309 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3310 return TRUE;
3313 sz = strlenW(tl_struct->source)+4;
3314 sz *= sizeof(WCHAR);
3316 if ((INT_PTR)lpszName == 1)
3317 tl_struct->path = strdupW(tl_struct->source);
3318 else
3320 tl_struct->path = msi_alloc(sz);
3321 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3324 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3325 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3326 if (FAILED(res))
3328 msi_free(tl_struct->path);
3329 tl_struct->path = NULL;
3331 return TRUE;
3334 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3335 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3337 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3338 return FALSE;
3341 msi_free(tl_struct->path);
3342 tl_struct->path = NULL;
3344 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3345 ITypeLib_Release(tl_struct->ptLib);
3347 return TRUE;
3350 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3352 MSIPACKAGE* package = param;
3353 LPCWSTR component;
3354 MSICOMPONENT *comp;
3355 MSIFILE *file;
3356 typelib_struct tl_struct;
3357 ITypeLib *tlib;
3358 HMODULE module;
3359 HRESULT hr;
3361 component = MSI_RecordGetString(row,3);
3362 comp = msi_get_loaded_component(package,component);
3363 if (!comp)
3364 return ERROR_SUCCESS;
3366 comp->Action = msi_get_component_action( package, comp );
3367 if (comp->Action != INSTALLSTATE_LOCAL)
3369 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3370 return ERROR_SUCCESS;
3373 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3375 TRACE("component has no key path\n");
3376 return ERROR_SUCCESS;
3378 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3380 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3381 if (module)
3383 LPCWSTR guid;
3384 guid = MSI_RecordGetString(row,1);
3385 CLSIDFromString((LPCWSTR)guid, &tl_struct.clsid);
3386 tl_struct.source = strdupW( file->TargetPath );
3387 tl_struct.path = NULL;
3389 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3390 (LONG_PTR)&tl_struct);
3392 if (tl_struct.path)
3394 LPCWSTR helpid, help_path = NULL;
3395 HRESULT res;
3397 helpid = MSI_RecordGetString(row,6);
3399 if (helpid) help_path = msi_get_target_folder( package, helpid );
3400 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3402 if (FAILED(res))
3403 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3404 else
3405 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3407 ITypeLib_Release(tl_struct.ptLib);
3408 msi_free(tl_struct.path);
3410 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3412 FreeLibrary(module);
3413 msi_free(tl_struct.source);
3415 else
3417 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3418 if (FAILED(hr))
3420 ERR("Failed to load type library: %08x\n", hr);
3421 return ERROR_INSTALL_FAILURE;
3424 ITypeLib_Release(tlib);
3427 return ERROR_SUCCESS;
3430 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3432 static const WCHAR query[] = {
3433 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3434 '`','T','y','p','e','L','i','b','`',0};
3435 MSIQUERY *view;
3436 UINT rc;
3438 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3439 if (rc != ERROR_SUCCESS)
3440 return ERROR_SUCCESS;
3442 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3443 msiobj_release(&view->hdr);
3444 return rc;
3447 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3449 MSIPACKAGE *package = param;
3450 LPCWSTR component, guid;
3451 MSICOMPONENT *comp;
3452 GUID libid;
3453 UINT version;
3454 LCID language;
3455 SYSKIND syskind;
3456 HRESULT hr;
3458 component = MSI_RecordGetString( row, 3 );
3459 comp = msi_get_loaded_component( package, component );
3460 if (!comp)
3461 return ERROR_SUCCESS;
3463 comp->Action = msi_get_component_action( package, comp );
3464 if (comp->Action != INSTALLSTATE_ABSENT)
3466 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3467 return ERROR_SUCCESS;
3469 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3471 guid = MSI_RecordGetString( row, 1 );
3472 CLSIDFromString( (LPCWSTR)guid, &libid );
3473 version = MSI_RecordGetInteger( row, 4 );
3474 language = MSI_RecordGetInteger( row, 2 );
3476 #ifdef _WIN64
3477 syskind = SYS_WIN64;
3478 #else
3479 syskind = SYS_WIN32;
3480 #endif
3482 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3483 if (FAILED(hr))
3485 WARN("Failed to unregister typelib: %08x\n", hr);
3488 return ERROR_SUCCESS;
3491 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3493 static const WCHAR query[] = {
3494 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3495 '`','T','y','p','e','L','i','b','`',0};
3496 MSIQUERY *view;
3497 UINT rc;
3499 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3500 if (rc != ERROR_SUCCESS)
3501 return ERROR_SUCCESS;
3503 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3504 msiobj_release( &view->hdr );
3505 return rc;
3508 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3510 static const WCHAR szlnk[] = {'.','l','n','k',0};
3511 LPCWSTR directory, extension, link_folder;
3512 LPWSTR link_file, filename;
3514 directory = MSI_RecordGetString( row, 2 );
3515 link_folder = msi_get_target_folder( package, directory );
3517 /* may be needed because of a bug somewhere else */
3518 msi_create_full_path( link_folder );
3520 filename = msi_dup_record_field( row, 3 );
3521 msi_reduce_to_long_filename( filename );
3523 extension = strchrW( filename, '.' );
3524 if (!extension || strcmpiW( extension, szlnk ))
3526 int len = strlenW( filename );
3527 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3528 memcpy( filename + len, szlnk, sizeof(szlnk) );
3530 link_file = msi_build_directory_name( 2, link_folder, filename );
3531 msi_free( filename );
3533 return link_file;
3536 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3538 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3539 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3540 WCHAR *folder, *dest, *path;
3542 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3543 folder = msi_dup_property( package->db, szWindowsFolder );
3544 else
3546 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3547 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3548 msi_free( appdata );
3550 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3551 msi_create_full_path( dest );
3552 path = msi_build_directory_name( 2, dest, icon_name );
3553 msi_free( folder );
3554 msi_free( dest );
3555 return path;
3558 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3560 MSIPACKAGE *package = param;
3561 LPWSTR link_file, deformated, path;
3562 LPCWSTR component, target;
3563 MSICOMPONENT *comp;
3564 IShellLinkW *sl = NULL;
3565 IPersistFile *pf = NULL;
3566 HRESULT res;
3568 component = MSI_RecordGetString(row, 4);
3569 comp = msi_get_loaded_component(package, component);
3570 if (!comp)
3571 return ERROR_SUCCESS;
3573 comp->Action = msi_get_component_action( package, comp );
3574 if (comp->Action != INSTALLSTATE_LOCAL)
3576 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3577 return ERROR_SUCCESS;
3579 msi_ui_actiondata( package, szCreateShortcuts, row );
3581 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3582 &IID_IShellLinkW, (LPVOID *) &sl );
3584 if (FAILED( res ))
3586 ERR("CLSID_ShellLink not available\n");
3587 goto err;
3590 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3591 if (FAILED( res ))
3593 ERR("QueryInterface(IID_IPersistFile) failed\n");
3594 goto err;
3597 target = MSI_RecordGetString(row, 5);
3598 if (strchrW(target, '['))
3600 deformat_string(package, target, &deformated);
3601 IShellLinkW_SetPath(sl,deformated);
3602 msi_free(deformated);
3604 else
3606 FIXME("poorly handled shortcut format, advertised shortcut\n");
3607 IShellLinkW_SetPath(sl,comp->FullKeypath);
3610 if (!MSI_RecordIsNull(row,6))
3612 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3613 deformat_string(package, arguments, &deformated);
3614 IShellLinkW_SetArguments(sl,deformated);
3615 msi_free(deformated);
3618 if (!MSI_RecordIsNull(row,7))
3620 LPCWSTR description = MSI_RecordGetString(row, 7);
3621 IShellLinkW_SetDescription(sl, description);
3624 if (!MSI_RecordIsNull(row,8))
3625 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3627 if (!MSI_RecordIsNull(row,9))
3629 INT index;
3630 LPCWSTR icon = MSI_RecordGetString(row, 9);
3632 path = msi_build_icon_path(package, icon);
3633 index = MSI_RecordGetInteger(row,10);
3635 /* no value means 0 */
3636 if (index == MSI_NULL_INTEGER)
3637 index = 0;
3639 IShellLinkW_SetIconLocation(sl, path, index);
3640 msi_free(path);
3643 if (!MSI_RecordIsNull(row,11))
3644 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3646 if (!MSI_RecordIsNull(row,12))
3648 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3649 full_path = msi_get_target_folder( package, wkdir );
3650 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3652 link_file = get_link_file(package, row);
3654 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3655 IPersistFile_Save(pf, link_file, FALSE);
3656 msi_free(link_file);
3658 err:
3659 if (pf)
3660 IPersistFile_Release( pf );
3661 if (sl)
3662 IShellLinkW_Release( sl );
3664 return ERROR_SUCCESS;
3667 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3669 static const WCHAR query[] = {
3670 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3671 '`','S','h','o','r','t','c','u','t','`',0};
3672 MSIQUERY *view;
3673 HRESULT res;
3674 UINT rc;
3676 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3677 if (rc != ERROR_SUCCESS)
3678 return ERROR_SUCCESS;
3680 res = CoInitialize( NULL );
3682 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3683 msiobj_release(&view->hdr);
3685 if (SUCCEEDED(res)) CoUninitialize();
3686 return rc;
3689 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3691 MSIPACKAGE *package = param;
3692 LPWSTR link_file;
3693 LPCWSTR component;
3694 MSICOMPONENT *comp;
3696 component = MSI_RecordGetString( row, 4 );
3697 comp = msi_get_loaded_component( package, component );
3698 if (!comp)
3699 return ERROR_SUCCESS;
3701 comp->Action = msi_get_component_action( package, comp );
3702 if (comp->Action != INSTALLSTATE_ABSENT)
3704 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3705 return ERROR_SUCCESS;
3707 msi_ui_actiondata( package, szRemoveShortcuts, row );
3709 link_file = get_link_file( package, row );
3711 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3712 if (!DeleteFileW( link_file ))
3714 WARN("Failed to remove shortcut file %u\n", GetLastError());
3716 msi_free( link_file );
3718 return ERROR_SUCCESS;
3721 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3723 static const WCHAR query[] = {
3724 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3725 '`','S','h','o','r','t','c','u','t','`',0};
3726 MSIQUERY *view;
3727 UINT rc;
3729 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3730 if (rc != ERROR_SUCCESS)
3731 return ERROR_SUCCESS;
3733 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3734 msiobj_release( &view->hdr );
3735 return rc;
3738 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3740 MSIPACKAGE* package = param;
3741 HANDLE the_file;
3742 LPWSTR FilePath;
3743 LPCWSTR FileName;
3744 CHAR buffer[1024];
3745 DWORD sz;
3746 UINT rc;
3748 FileName = MSI_RecordGetString(row,1);
3749 if (!FileName)
3751 ERR("Unable to get FileName\n");
3752 return ERROR_SUCCESS;
3755 FilePath = msi_build_icon_path(package, FileName);
3757 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3759 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3760 FILE_ATTRIBUTE_NORMAL, NULL);
3762 if (the_file == INVALID_HANDLE_VALUE)
3764 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3765 msi_free(FilePath);
3766 return ERROR_SUCCESS;
3771 DWORD write;
3772 sz = 1024;
3773 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3774 if (rc != ERROR_SUCCESS)
3776 ERR("Failed to get stream\n");
3777 CloseHandle(the_file);
3778 DeleteFileW(FilePath);
3779 break;
3781 WriteFile(the_file,buffer,sz,&write,NULL);
3782 } while (sz == 1024);
3784 msi_free(FilePath);
3785 CloseHandle(the_file);
3787 return ERROR_SUCCESS;
3790 static UINT msi_publish_icons(MSIPACKAGE *package)
3792 static const WCHAR query[]= {
3793 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3794 '`','I','c','o','n','`',0};
3795 MSIQUERY *view;
3796 UINT r;
3798 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3799 if (r == ERROR_SUCCESS)
3801 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3802 msiobj_release(&view->hdr);
3804 return ERROR_SUCCESS;
3807 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3809 UINT r;
3810 HKEY source;
3811 LPWSTR buffer;
3812 MSIMEDIADISK *disk;
3813 MSISOURCELISTINFO *info;
3815 r = RegCreateKeyW(hkey, szSourceList, &source);
3816 if (r != ERROR_SUCCESS)
3817 return r;
3819 RegCloseKey(source);
3821 buffer = strrchrW(package->PackagePath, '\\') + 1;
3822 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3823 package->Context, MSICODE_PRODUCT,
3824 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3825 if (r != ERROR_SUCCESS)
3826 return r;
3828 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3829 package->Context, MSICODE_PRODUCT,
3830 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3831 if (r != ERROR_SUCCESS)
3832 return r;
3834 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3835 package->Context, MSICODE_PRODUCT,
3836 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3837 if (r != ERROR_SUCCESS)
3838 return r;
3840 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3842 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3843 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3844 info->options, info->value);
3845 else
3846 MsiSourceListSetInfoW(package->ProductCode, NULL,
3847 info->context, info->options,
3848 info->property, info->value);
3851 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3853 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3854 disk->context, disk->options,
3855 disk->disk_id, disk->volume_label, disk->disk_prompt);
3858 return ERROR_SUCCESS;
3861 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3863 MSIHANDLE hdb, suminfo;
3864 WCHAR guids[MAX_PATH];
3865 WCHAR packcode[SQUISH_GUID_SIZE];
3866 LPWSTR buffer;
3867 LPWSTR ptr;
3868 DWORD langid;
3869 DWORD size;
3870 UINT r;
3872 static const WCHAR szARPProductIcon[] =
3873 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3874 static const WCHAR szAssignment[] =
3875 {'A','s','s','i','g','n','m','e','n','t',0};
3876 static const WCHAR szAdvertiseFlags[] =
3877 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3878 static const WCHAR szClients[] =
3879 {'C','l','i','e','n','t','s',0};
3880 static const WCHAR szColon[] = {':',0};
3882 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3883 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3884 msi_free(buffer);
3886 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3887 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3889 /* FIXME */
3890 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3892 buffer = msi_dup_property(package->db, szARPProductIcon);
3893 if (buffer)
3895 LPWSTR path = msi_build_icon_path(package, buffer);
3896 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3897 msi_free(path);
3898 msi_free(buffer);
3901 buffer = msi_dup_property(package->db, szProductVersion);
3902 if (buffer)
3904 DWORD verdword = msi_version_str_to_dword(buffer);
3905 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3906 msi_free(buffer);
3909 msi_reg_set_val_dword(hkey, szAssignment, 0);
3910 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3911 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3912 msi_reg_set_val_str(hkey, szClients, szColon);
3914 hdb = alloc_msihandle(&package->db->hdr);
3915 if (!hdb)
3916 return ERROR_NOT_ENOUGH_MEMORY;
3918 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3919 MsiCloseHandle(hdb);
3920 if (r != ERROR_SUCCESS)
3921 goto done;
3923 size = MAX_PATH;
3924 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3925 NULL, guids, &size);
3926 if (r != ERROR_SUCCESS)
3927 goto done;
3929 ptr = strchrW(guids, ';');
3930 if (ptr) *ptr = 0;
3931 squash_guid(guids, packcode);
3932 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3934 done:
3935 MsiCloseHandle(suminfo);
3936 return ERROR_SUCCESS;
3939 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3941 UINT r;
3942 HKEY hkey;
3943 LPWSTR upgrade;
3944 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3946 upgrade = msi_dup_property(package->db, szUpgradeCode);
3947 if (!upgrade)
3948 return ERROR_SUCCESS;
3950 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3951 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3952 else
3953 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3955 if (r != ERROR_SUCCESS)
3957 WARN("failed to open upgrade code key\n");
3958 msi_free(upgrade);
3959 return ERROR_SUCCESS;
3961 squash_guid(package->ProductCode, squashed_pc);
3962 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3963 RegCloseKey(hkey);
3964 msi_free(upgrade);
3965 return ERROR_SUCCESS;
3968 static BOOL msi_check_publish(MSIPACKAGE *package)
3970 MSIFEATURE *feature;
3972 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3974 feature->Action = msi_get_feature_action( package, feature );
3975 if (feature->Action == INSTALLSTATE_LOCAL)
3976 return TRUE;
3979 return FALSE;
3982 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3984 MSIFEATURE *feature;
3986 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3988 feature->Action = msi_get_feature_action( package, feature );
3989 if (feature->Action != INSTALLSTATE_ABSENT)
3990 return FALSE;
3993 return TRUE;
3996 static UINT msi_publish_patches( MSIPACKAGE *package )
3998 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3999 WCHAR patch_squashed[GUID_SIZE];
4000 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4001 LONG res;
4002 MSIPATCHINFO *patch;
4003 UINT r;
4004 WCHAR *p, *all_patches = NULL;
4005 DWORD len = 0;
4007 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4008 if (r != ERROR_SUCCESS)
4009 return ERROR_FUNCTION_FAILED;
4011 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4012 if (res != ERROR_SUCCESS)
4014 r = ERROR_FUNCTION_FAILED;
4015 goto done;
4018 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4019 if (r != ERROR_SUCCESS)
4020 goto done;
4022 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4024 squash_guid( patch->patchcode, patch_squashed );
4025 len += strlenW( patch_squashed ) + 1;
4028 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4029 if (!all_patches)
4030 goto done;
4032 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4034 HKEY patch_key;
4036 squash_guid( patch->patchcode, p );
4037 p += strlenW( p ) + 1;
4039 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4040 (const BYTE *)patch->transforms,
4041 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4042 if (res != ERROR_SUCCESS)
4043 goto done;
4045 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4046 if (r != ERROR_SUCCESS)
4047 goto done;
4049 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4050 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4051 RegCloseKey( patch_key );
4052 if (res != ERROR_SUCCESS)
4053 goto done;
4055 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4057 res = GetLastError();
4058 ERR("Unable to copy patch package %d\n", res);
4059 goto done;
4061 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4062 if (res != ERROR_SUCCESS)
4063 goto done;
4065 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4066 RegCloseKey( patch_key );
4067 if (res != ERROR_SUCCESS)
4068 goto done;
4071 all_patches[len] = 0;
4072 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4073 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4074 if (res != ERROR_SUCCESS)
4075 goto done;
4077 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4078 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4079 if (res != ERROR_SUCCESS)
4080 r = ERROR_FUNCTION_FAILED;
4082 done:
4083 RegCloseKey( product_patches_key );
4084 RegCloseKey( patches_key );
4085 RegCloseKey( product_key );
4086 msi_free( all_patches );
4087 return r;
4090 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4092 UINT rc;
4093 HKEY hukey = NULL, hudkey = NULL;
4094 MSIRECORD *uirow;
4096 if (!list_empty(&package->patches))
4098 rc = msi_publish_patches(package);
4099 if (rc != ERROR_SUCCESS)
4100 goto end;
4103 /* FIXME: also need to publish if the product is in advertise mode */
4104 if (!msi_check_publish(package))
4105 return ERROR_SUCCESS;
4107 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4108 &hukey, TRUE);
4109 if (rc != ERROR_SUCCESS)
4110 goto end;
4112 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4113 NULL, &hudkey, TRUE);
4114 if (rc != ERROR_SUCCESS)
4115 goto end;
4117 rc = msi_publish_upgrade_code(package);
4118 if (rc != ERROR_SUCCESS)
4119 goto end;
4121 rc = msi_publish_product_properties(package, hukey);
4122 if (rc != ERROR_SUCCESS)
4123 goto end;
4125 rc = msi_publish_sourcelist(package, hukey);
4126 if (rc != ERROR_SUCCESS)
4127 goto end;
4129 rc = msi_publish_icons(package);
4131 end:
4132 uirow = MSI_CreateRecord( 1 );
4133 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4134 msi_ui_actiondata( package, szPublishProduct, uirow );
4135 msiobj_release( &uirow->hdr );
4137 RegCloseKey(hukey);
4138 RegCloseKey(hudkey);
4139 return rc;
4142 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4144 WCHAR *filename, *ptr, *folder, *ret;
4145 const WCHAR *dirprop;
4147 filename = msi_dup_record_field( row, 2 );
4148 if (filename && (ptr = strchrW( filename, '|' )))
4149 ptr++;
4150 else
4151 ptr = filename;
4153 dirprop = MSI_RecordGetString( row, 3 );
4154 if (dirprop)
4156 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4157 if (!folder) folder = msi_dup_property( package->db, dirprop );
4159 else
4160 folder = msi_dup_property( package->db, szWindowsFolder );
4162 if (!folder)
4164 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4165 msi_free( filename );
4166 return NULL;
4169 ret = msi_build_directory_name( 2, folder, ptr );
4171 msi_free( filename );
4172 msi_free( folder );
4173 return ret;
4176 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4178 MSIPACKAGE *package = param;
4179 LPCWSTR component, section, key, value, identifier;
4180 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4181 MSIRECORD * uirow;
4182 INT action;
4183 MSICOMPONENT *comp;
4185 component = MSI_RecordGetString(row, 8);
4186 comp = msi_get_loaded_component(package,component);
4187 if (!comp)
4188 return ERROR_SUCCESS;
4190 comp->Action = msi_get_component_action( package, comp );
4191 if (comp->Action != INSTALLSTATE_LOCAL)
4193 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4194 return ERROR_SUCCESS;
4197 identifier = MSI_RecordGetString(row,1);
4198 section = MSI_RecordGetString(row,4);
4199 key = MSI_RecordGetString(row,5);
4200 value = MSI_RecordGetString(row,6);
4201 action = MSI_RecordGetInteger(row,7);
4203 deformat_string(package,section,&deformated_section);
4204 deformat_string(package,key,&deformated_key);
4205 deformat_string(package,value,&deformated_value);
4207 fullname = get_ini_file_name(package, row);
4209 if (action == 0)
4211 TRACE("Adding value %s to section %s in %s\n",
4212 debugstr_w(deformated_key), debugstr_w(deformated_section),
4213 debugstr_w(fullname));
4214 WritePrivateProfileStringW(deformated_section, deformated_key,
4215 deformated_value, fullname);
4217 else if (action == 1)
4219 WCHAR returned[10];
4220 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4221 returned, 10, fullname);
4222 if (returned[0] == 0)
4224 TRACE("Adding value %s to section %s in %s\n",
4225 debugstr_w(deformated_key), debugstr_w(deformated_section),
4226 debugstr_w(fullname));
4228 WritePrivateProfileStringW(deformated_section, deformated_key,
4229 deformated_value, fullname);
4232 else if (action == 3)
4233 FIXME("Append to existing section not yet implemented\n");
4235 uirow = MSI_CreateRecord(4);
4236 MSI_RecordSetStringW(uirow,1,identifier);
4237 MSI_RecordSetStringW(uirow,2,deformated_section);
4238 MSI_RecordSetStringW(uirow,3,deformated_key);
4239 MSI_RecordSetStringW(uirow,4,deformated_value);
4240 msi_ui_actiondata( package, szWriteIniValues, uirow );
4241 msiobj_release( &uirow->hdr );
4243 msi_free(fullname);
4244 msi_free(deformated_key);
4245 msi_free(deformated_value);
4246 msi_free(deformated_section);
4247 return ERROR_SUCCESS;
4250 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4252 static const WCHAR query[] = {
4253 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4254 '`','I','n','i','F','i','l','e','`',0};
4255 MSIQUERY *view;
4256 UINT rc;
4258 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4259 if (rc != ERROR_SUCCESS)
4260 return ERROR_SUCCESS;
4262 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4263 msiobj_release(&view->hdr);
4264 return rc;
4267 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4269 MSIPACKAGE *package = param;
4270 LPCWSTR component, section, key, value, identifier;
4271 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4272 MSICOMPONENT *comp;
4273 MSIRECORD *uirow;
4274 INT action;
4276 component = MSI_RecordGetString( row, 8 );
4277 comp = msi_get_loaded_component( package, component );
4278 if (!comp)
4279 return ERROR_SUCCESS;
4281 comp->Action = msi_get_component_action( package, comp );
4282 if (comp->Action != INSTALLSTATE_ABSENT)
4284 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4285 return ERROR_SUCCESS;
4288 identifier = MSI_RecordGetString( row, 1 );
4289 section = MSI_RecordGetString( row, 4 );
4290 key = MSI_RecordGetString( row, 5 );
4291 value = MSI_RecordGetString( row, 6 );
4292 action = MSI_RecordGetInteger( row, 7 );
4294 deformat_string( package, section, &deformated_section );
4295 deformat_string( package, key, &deformated_key );
4296 deformat_string( package, value, &deformated_value );
4298 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4300 filename = get_ini_file_name( package, row );
4302 TRACE("Removing key %s from section %s in %s\n",
4303 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4305 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4307 WARN("Unable to remove key %u\n", GetLastError());
4309 msi_free( filename );
4311 else
4312 FIXME("Unsupported action %d\n", action);
4315 uirow = MSI_CreateRecord( 4 );
4316 MSI_RecordSetStringW( uirow, 1, identifier );
4317 MSI_RecordSetStringW( uirow, 2, deformated_section );
4318 MSI_RecordSetStringW( uirow, 3, deformated_key );
4319 MSI_RecordSetStringW( uirow, 4, deformated_value );
4320 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4321 msiobj_release( &uirow->hdr );
4323 msi_free( deformated_key );
4324 msi_free( deformated_value );
4325 msi_free( deformated_section );
4326 return ERROR_SUCCESS;
4329 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4331 MSIPACKAGE *package = param;
4332 LPCWSTR component, section, key, value, identifier;
4333 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4334 MSICOMPONENT *comp;
4335 MSIRECORD *uirow;
4336 INT action;
4338 component = MSI_RecordGetString( row, 8 );
4339 comp = msi_get_loaded_component( package, component );
4340 if (!comp)
4341 return ERROR_SUCCESS;
4343 comp->Action = msi_get_component_action( package, comp );
4344 if (comp->Action != INSTALLSTATE_LOCAL)
4346 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4347 return ERROR_SUCCESS;
4350 identifier = MSI_RecordGetString( row, 1 );
4351 section = MSI_RecordGetString( row, 4 );
4352 key = MSI_RecordGetString( row, 5 );
4353 value = MSI_RecordGetString( row, 6 );
4354 action = MSI_RecordGetInteger( row, 7 );
4356 deformat_string( package, section, &deformated_section );
4357 deformat_string( package, key, &deformated_key );
4358 deformat_string( package, value, &deformated_value );
4360 if (action == msidbIniFileActionRemoveLine)
4362 filename = get_ini_file_name( package, row );
4364 TRACE("Removing key %s from section %s in %s\n",
4365 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4367 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4369 WARN("Unable to remove key %u\n", GetLastError());
4371 msi_free( filename );
4373 else
4374 FIXME("Unsupported action %d\n", action);
4376 uirow = MSI_CreateRecord( 4 );
4377 MSI_RecordSetStringW( uirow, 1, identifier );
4378 MSI_RecordSetStringW( uirow, 2, deformated_section );
4379 MSI_RecordSetStringW( uirow, 3, deformated_key );
4380 MSI_RecordSetStringW( uirow, 4, deformated_value );
4381 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4382 msiobj_release( &uirow->hdr );
4384 msi_free( deformated_key );
4385 msi_free( deformated_value );
4386 msi_free( deformated_section );
4387 return ERROR_SUCCESS;
4390 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4392 static const WCHAR query[] = {
4393 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4394 '`','I','n','i','F','i','l','e','`',0};
4395 static const WCHAR remove_query[] = {
4396 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4397 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4398 MSIQUERY *view;
4399 UINT rc;
4401 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4402 if (rc == ERROR_SUCCESS)
4404 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4405 msiobj_release( &view->hdr );
4406 if (rc != ERROR_SUCCESS)
4407 return rc;
4409 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4410 if (rc == ERROR_SUCCESS)
4412 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4413 msiobj_release( &view->hdr );
4414 if (rc != ERROR_SUCCESS)
4415 return rc;
4417 return ERROR_SUCCESS;
4420 static void register_dll( const WCHAR *dll, BOOL unregister )
4422 HMODULE hmod;
4424 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4425 if (hmod)
4427 HRESULT (WINAPI *func_ptr)( void );
4428 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4430 func_ptr = (void *)GetProcAddress( hmod, func );
4431 if (func_ptr)
4433 HRESULT hr = func_ptr();
4434 if (FAILED( hr ))
4435 WARN("failed to register dll 0x%08x\n", hr);
4437 else
4438 WARN("entry point %s not found\n", func);
4439 FreeLibrary( hmod );
4440 return;
4442 WARN("failed to load library %u\n", GetLastError());
4445 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4447 MSIPACKAGE *package = param;
4448 LPCWSTR filename;
4449 MSIFILE *file;
4450 MSIRECORD *uirow;
4452 filename = MSI_RecordGetString(row,1);
4453 file = msi_get_loaded_file( package, filename );
4454 if (!file)
4456 WARN("unable to find file %s\n", debugstr_w(filename));
4457 return ERROR_SUCCESS;
4459 file->Component->Action = msi_get_component_action( package, file->Component );
4460 if (file->Component->Action != INSTALLSTATE_LOCAL)
4462 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4463 return ERROR_SUCCESS;
4466 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4467 register_dll( file->TargetPath, FALSE );
4469 uirow = MSI_CreateRecord( 2 );
4470 MSI_RecordSetStringW( uirow, 1, filename );
4471 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4472 msi_ui_actiondata( package, szSelfRegModules, uirow );
4473 msiobj_release( &uirow->hdr );
4475 return ERROR_SUCCESS;
4478 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4480 static const WCHAR query[] = {
4481 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4482 '`','S','e','l','f','R','e','g','`',0};
4483 MSIQUERY *view;
4484 UINT rc;
4486 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4487 if (rc != ERROR_SUCCESS)
4488 return ERROR_SUCCESS;
4490 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4491 msiobj_release(&view->hdr);
4492 return ERROR_SUCCESS;
4495 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4497 MSIPACKAGE *package = param;
4498 LPCWSTR filename;
4499 MSIFILE *file;
4500 MSIRECORD *uirow;
4502 filename = MSI_RecordGetString( row, 1 );
4503 file = msi_get_loaded_file( package, filename );
4504 if (!file)
4506 WARN("unable to find file %s\n", debugstr_w(filename));
4507 return ERROR_SUCCESS;
4509 file->Component->Action = msi_get_component_action( package, file->Component );
4510 if (file->Component->Action != INSTALLSTATE_ABSENT)
4512 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4513 return ERROR_SUCCESS;
4516 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4517 register_dll( file->TargetPath, TRUE );
4519 uirow = MSI_CreateRecord( 2 );
4520 MSI_RecordSetStringW( uirow, 1, filename );
4521 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4522 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4523 msiobj_release( &uirow->hdr );
4525 return ERROR_SUCCESS;
4528 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4530 static const WCHAR query[] = {
4531 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4532 '`','S','e','l','f','R','e','g','`',0};
4533 MSIQUERY *view;
4534 UINT rc;
4536 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4537 if (rc != ERROR_SUCCESS)
4538 return ERROR_SUCCESS;
4540 MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4541 msiobj_release( &view->hdr );
4542 return ERROR_SUCCESS;
4545 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4547 MSIFEATURE *feature;
4548 UINT rc;
4549 HKEY hkey = NULL, userdata = NULL;
4551 if (!msi_check_publish(package))
4552 return ERROR_SUCCESS;
4554 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4555 &hkey, TRUE);
4556 if (rc != ERROR_SUCCESS)
4557 goto end;
4559 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4560 &userdata, TRUE);
4561 if (rc != ERROR_SUCCESS)
4562 goto end;
4564 /* here the guids are base 85 encoded */
4565 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4567 ComponentList *cl;
4568 LPWSTR data = NULL;
4569 GUID clsid;
4570 INT size;
4571 BOOL absent = FALSE;
4572 MSIRECORD *uirow;
4574 if (feature->Action != INSTALLSTATE_LOCAL &&
4575 feature->Action != INSTALLSTATE_SOURCE &&
4576 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4578 size = 1;
4579 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4581 size += 21;
4583 if (feature->Feature_Parent)
4584 size += strlenW( feature->Feature_Parent )+2;
4586 data = msi_alloc(size * sizeof(WCHAR));
4588 data[0] = 0;
4589 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4591 MSICOMPONENT* component = cl->component;
4592 WCHAR buf[21];
4594 buf[0] = 0;
4595 if (component->ComponentId)
4597 TRACE("From %s\n",debugstr_w(component->ComponentId));
4598 CLSIDFromString(component->ComponentId, &clsid);
4599 encode_base85_guid(&clsid,buf);
4600 TRACE("to %s\n",debugstr_w(buf));
4601 strcatW(data,buf);
4605 if (feature->Feature_Parent)
4607 static const WCHAR sep[] = {'\2',0};
4608 strcatW(data,sep);
4609 strcatW(data,feature->Feature_Parent);
4612 msi_reg_set_val_str( userdata, feature->Feature, data );
4613 msi_free(data);
4615 size = 0;
4616 if (feature->Feature_Parent)
4617 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4618 if (!absent)
4620 size += sizeof(WCHAR);
4621 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4622 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4624 else
4626 size += 2*sizeof(WCHAR);
4627 data = msi_alloc(size);
4628 data[0] = 0x6;
4629 data[1] = 0;
4630 if (feature->Feature_Parent)
4631 strcpyW( &data[1], feature->Feature_Parent );
4632 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4633 (LPBYTE)data,size);
4634 msi_free(data);
4637 /* the UI chunk */
4638 uirow = MSI_CreateRecord( 1 );
4639 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4640 msi_ui_actiondata( package, szPublishFeatures, uirow );
4641 msiobj_release( &uirow->hdr );
4642 /* FIXME: call msi_ui_progress? */
4645 end:
4646 RegCloseKey(hkey);
4647 RegCloseKey(userdata);
4648 return rc;
4651 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4653 UINT r;
4654 HKEY hkey;
4655 MSIRECORD *uirow;
4657 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4659 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4660 &hkey, FALSE);
4661 if (r == ERROR_SUCCESS)
4663 RegDeleteValueW(hkey, feature->Feature);
4664 RegCloseKey(hkey);
4667 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4668 &hkey, FALSE);
4669 if (r == ERROR_SUCCESS)
4671 RegDeleteValueW(hkey, feature->Feature);
4672 RegCloseKey(hkey);
4675 uirow = MSI_CreateRecord( 1 );
4676 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4677 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4678 msiobj_release( &uirow->hdr );
4680 return ERROR_SUCCESS;
4683 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4685 MSIFEATURE *feature;
4687 if (!msi_check_unpublish(package))
4688 return ERROR_SUCCESS;
4690 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4692 msi_unpublish_feature(package, feature);
4695 return ERROR_SUCCESS;
4698 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4700 SYSTEMTIME systime;
4701 DWORD size, langid;
4702 WCHAR date[9], *val, *buffer;
4703 const WCHAR *prop, *key;
4705 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4706 static const WCHAR modpath_fmt[] =
4707 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4708 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4709 static const WCHAR szModifyPath[] =
4710 {'M','o','d','i','f','y','P','a','t','h',0};
4711 static const WCHAR szUninstallString[] =
4712 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4713 static const WCHAR szEstimatedSize[] =
4714 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4715 static const WCHAR szDisplayVersion[] =
4716 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4717 static const WCHAR szInstallSource[] =
4718 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4719 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4720 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4721 static const WCHAR szAuthorizedCDFPrefix[] =
4722 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4723 static const WCHAR szARPCONTACT[] =
4724 {'A','R','P','C','O','N','T','A','C','T',0};
4725 static const WCHAR szContact[] =
4726 {'C','o','n','t','a','c','t',0};
4727 static const WCHAR szARPCOMMENTS[] =
4728 {'A','R','P','C','O','M','M','E','N','T','S',0};
4729 static const WCHAR szComments[] =
4730 {'C','o','m','m','e','n','t','s',0};
4731 static const WCHAR szProductName[] =
4732 {'P','r','o','d','u','c','t','N','a','m','e',0};
4733 static const WCHAR szDisplayName[] =
4734 {'D','i','s','p','l','a','y','N','a','m','e',0};
4735 static const WCHAR szARPHELPLINK[] =
4736 {'A','R','P','H','E','L','P','L','I','N','K',0};
4737 static const WCHAR szHelpLink[] =
4738 {'H','e','l','p','L','i','n','k',0};
4739 static const WCHAR szARPHELPTELEPHONE[] =
4740 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4741 static const WCHAR szHelpTelephone[] =
4742 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4743 static const WCHAR szARPINSTALLLOCATION[] =
4744 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4745 static const WCHAR szInstallLocation[] =
4746 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4747 static const WCHAR szManufacturer[] =
4748 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4749 static const WCHAR szPublisher[] =
4750 {'P','u','b','l','i','s','h','e','r',0};
4751 static const WCHAR szARPREADME[] =
4752 {'A','R','P','R','E','A','D','M','E',0};
4753 static const WCHAR szReadme[] =
4754 {'R','e','a','d','M','e',0};
4755 static const WCHAR szARPSIZE[] =
4756 {'A','R','P','S','I','Z','E',0};
4757 static const WCHAR szSize[] =
4758 {'S','i','z','e',0};
4759 static const WCHAR szARPURLINFOABOUT[] =
4760 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4761 static const WCHAR szURLInfoAbout[] =
4762 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4763 static const WCHAR szARPURLUPDATEINFO[] =
4764 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4765 static const WCHAR szURLUpdateInfo[] =
4766 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4767 static const WCHAR szARPSYSTEMCOMPONENT[] =
4768 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4769 static const WCHAR szSystemComponent[] =
4770 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4772 static const WCHAR *propval[] = {
4773 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4774 szARPCONTACT, szContact,
4775 szARPCOMMENTS, szComments,
4776 szProductName, szDisplayName,
4777 szARPHELPLINK, szHelpLink,
4778 szARPHELPTELEPHONE, szHelpTelephone,
4779 szARPINSTALLLOCATION, szInstallLocation,
4780 szSourceDir, szInstallSource,
4781 szManufacturer, szPublisher,
4782 szARPREADME, szReadme,
4783 szARPSIZE, szSize,
4784 szARPURLINFOABOUT, szURLInfoAbout,
4785 szARPURLUPDATEINFO, szURLUpdateInfo,
4786 NULL
4788 const WCHAR **p = propval;
4790 while (*p)
4792 prop = *p++;
4793 key = *p++;
4794 val = msi_dup_property(package->db, prop);
4795 msi_reg_set_val_str(hkey, key, val);
4796 msi_free(val);
4799 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4800 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4802 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4804 size = deformat_string(package, modpath_fmt, &buffer);
4805 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4806 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4807 msi_free(buffer);
4809 /* FIXME: Write real Estimated Size when we have it */
4810 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4812 GetLocalTime(&systime);
4813 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4814 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4816 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4817 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4819 buffer = msi_dup_property(package->db, szProductVersion);
4820 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4821 if (buffer)
4823 DWORD verdword = msi_version_str_to_dword(buffer);
4825 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4826 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4827 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4828 msi_free(buffer);
4831 return ERROR_SUCCESS;
4834 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4836 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4837 MSIRECORD *uirow;
4838 LPWSTR upgrade_code;
4839 HKEY hkey, props, upgrade_key;
4840 UINT rc;
4842 /* FIXME: also need to publish if the product is in advertise mode */
4843 if (!msi_check_publish(package))
4844 return ERROR_SUCCESS;
4846 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4847 if (rc != ERROR_SUCCESS)
4848 return rc;
4850 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4851 if (rc != ERROR_SUCCESS)
4852 goto done;
4854 if (!msi_get_property_int( package->db, szInstalled, 0 ))
4856 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4857 if (!CopyFileW( package->PackagePath, package->localfile, FALSE ))
4859 rc = GetLastError();
4860 ERR("Unable to copy package %u\n", rc);
4861 goto done;
4864 msi_free( package->localfile );
4865 package->localfile = NULL;
4867 rc = msi_publish_install_properties(package, hkey);
4868 if (rc != ERROR_SUCCESS)
4869 goto done;
4871 rc = msi_publish_install_properties(package, props);
4872 if (rc != ERROR_SUCCESS)
4873 goto done;
4875 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4876 if (upgrade_code)
4878 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4879 if (rc == ERROR_SUCCESS)
4881 squash_guid( package->ProductCode, squashed_pc );
4882 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4883 RegCloseKey( upgrade_key );
4885 msi_free( upgrade_code );
4888 done:
4889 uirow = MSI_CreateRecord( 1 );
4890 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4891 msi_ui_actiondata( package, szRegisterProduct, uirow );
4892 msiobj_release( &uirow->hdr );
4894 RegCloseKey(hkey);
4895 return ERROR_SUCCESS;
4898 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4900 return execute_script(package,INSTALL_SCRIPT);
4903 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4905 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4906 WCHAR *upgrade, **features;
4907 BOOL full_uninstall = TRUE;
4908 MSIFEATURE *feature;
4909 MSIPATCHINFO *patch;
4910 UINT i;
4912 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4914 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4916 features = msi_split_string( remove, ',' );
4917 for (i = 0; features && features[i]; i++)
4919 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4921 msi_free(features);
4923 if (!full_uninstall)
4924 return ERROR_SUCCESS;
4926 MSIREG_DeleteProductKey(package->ProductCode);
4927 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4928 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4930 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4931 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4932 MSIREG_DeleteUserProductKey(package->ProductCode);
4933 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4935 upgrade = msi_dup_property(package->db, szUpgradeCode);
4936 if (upgrade)
4938 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4939 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4940 msi_free(upgrade);
4943 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4945 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4946 /* FIXME: remove local patch package if this is the last product */
4949 TRACE("removing local package %s\n", debugstr_w(package->localfile));
4950 DeleteFileW( package->localfile );
4951 msi_free( package->localfile );
4952 package->localfile = NULL;
4954 return ERROR_SUCCESS;
4957 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4959 UINT rc;
4960 WCHAR *remove;
4962 /* turn off scheduling */
4963 package->script->CurrentlyScripting= FALSE;
4965 /* first do the same as an InstallExecute */
4966 rc = ACTION_InstallExecute(package);
4967 if (rc != ERROR_SUCCESS)
4968 return rc;
4970 /* then handle Commit Actions */
4971 rc = execute_script(package,COMMIT_SCRIPT);
4972 if (rc != ERROR_SUCCESS)
4973 return rc;
4975 remove = msi_dup_property(package->db, szRemove);
4976 rc = msi_unpublish_product(package, remove);
4977 msi_free(remove);
4978 return rc;
4981 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4983 static const WCHAR RunOnce[] = {
4984 'S','o','f','t','w','a','r','e','\\',
4985 'M','i','c','r','o','s','o','f','t','\\',
4986 'W','i','n','d','o','w','s','\\',
4987 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4988 'R','u','n','O','n','c','e',0};
4989 static const WCHAR InstallRunOnce[] = {
4990 'S','o','f','t','w','a','r','e','\\',
4991 'M','i','c','r','o','s','o','f','t','\\',
4992 'W','i','n','d','o','w','s','\\',
4993 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4994 'I','n','s','t','a','l','l','e','r','\\',
4995 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4997 static const WCHAR msiexec_fmt[] = {
4998 '%','s',
4999 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5000 '\"','%','s','\"',0};
5001 static const WCHAR install_fmt[] = {
5002 '/','I',' ','\"','%','s','\"',' ',
5003 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5004 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5005 WCHAR buffer[256], sysdir[MAX_PATH];
5006 HKEY hkey;
5007 WCHAR squished_pc[100];
5009 squash_guid(package->ProductCode,squished_pc);
5011 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5012 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5013 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5014 squished_pc);
5016 msi_reg_set_val_str( hkey, squished_pc, buffer );
5017 RegCloseKey(hkey);
5019 TRACE("Reboot command %s\n",debugstr_w(buffer));
5021 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5022 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5024 msi_reg_set_val_str( hkey, squished_pc, buffer );
5025 RegCloseKey(hkey);
5027 return ERROR_INSTALL_SUSPEND;
5030 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5032 static const WCHAR query[] =
5033 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5034 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5035 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5036 MSIRECORD *rec, *row;
5037 DWORD i, size = 0;
5038 va_list va;
5039 const WCHAR *str;
5040 WCHAR *data;
5042 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5044 rec = MSI_CreateRecord( count + 2 );
5045 str = MSI_RecordGetString( row, 1 );
5046 MSI_RecordSetStringW( rec, 0, str );
5047 msiobj_release( &row->hdr );
5048 MSI_RecordSetInteger( rec, 1, error );
5050 va_start( va, count );
5051 for (i = 0; i < count; i++)
5053 str = va_arg( va, const WCHAR *);
5054 MSI_RecordSetStringW( rec, i + 2, str );
5056 va_end( va );
5058 MSI_FormatRecordW( package, rec, NULL, &size );
5059 size++;
5060 data = msi_alloc( size * sizeof(WCHAR) );
5061 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5062 else data[0] = 0;
5063 msiobj_release( &rec->hdr );
5064 return data;
5067 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5069 DWORD attrib;
5070 UINT rc;
5073 * We are currently doing what should be done here in the top level Install
5074 * however for Administrative and uninstalls this step will be needed
5076 if (!package->PackagePath)
5077 return ERROR_SUCCESS;
5079 msi_set_sourcedir_props(package, TRUE);
5081 attrib = GetFileAttributesW(package->db->path);
5082 if (attrib == INVALID_FILE_ATTRIBUTES)
5084 LPWSTR prompt;
5085 LPWSTR msg;
5086 DWORD size = 0;
5088 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5089 package->Context, MSICODE_PRODUCT,
5090 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5091 if (rc == ERROR_MORE_DATA)
5093 prompt = msi_alloc(size * sizeof(WCHAR));
5094 MsiSourceListGetInfoW(package->ProductCode, NULL,
5095 package->Context, MSICODE_PRODUCT,
5096 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5098 else
5099 prompt = strdupW(package->db->path);
5101 msg = msi_build_error_string(package, 1302, 1, prompt);
5102 while(attrib == INVALID_FILE_ATTRIBUTES)
5104 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
5105 if (rc == IDCANCEL)
5107 rc = ERROR_INSTALL_USEREXIT;
5108 break;
5110 attrib = GetFileAttributesW(package->db->path);
5112 msi_free(prompt);
5113 rc = ERROR_SUCCESS;
5115 else
5116 return ERROR_SUCCESS;
5118 return rc;
5121 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5123 HKEY hkey = 0;
5124 LPWSTR buffer, productid = NULL;
5125 UINT i, rc = ERROR_SUCCESS;
5126 MSIRECORD *uirow;
5128 static const WCHAR szPropKeys[][80] =
5130 {'P','r','o','d','u','c','t','I','D',0},
5131 {'U','S','E','R','N','A','M','E',0},
5132 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5133 {0},
5136 static const WCHAR szRegKeys[][80] =
5138 {'P','r','o','d','u','c','t','I','D',0},
5139 {'R','e','g','O','w','n','e','r',0},
5140 {'R','e','g','C','o','m','p','a','n','y',0},
5141 {0},
5144 if (msi_check_unpublish(package))
5146 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5147 goto end;
5150 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5151 if (!productid)
5152 goto end;
5154 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5155 NULL, &hkey, TRUE);
5156 if (rc != ERROR_SUCCESS)
5157 goto end;
5159 for( i = 0; szPropKeys[i][0]; i++ )
5161 buffer = msi_dup_property( package->db, szPropKeys[i] );
5162 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5163 msi_free( buffer );
5166 end:
5167 uirow = MSI_CreateRecord( 1 );
5168 MSI_RecordSetStringW( uirow, 1, productid );
5169 msi_ui_actiondata( package, szRegisterUser, uirow );
5170 msiobj_release( &uirow->hdr );
5172 msi_free(productid);
5173 RegCloseKey(hkey);
5174 return rc;
5178 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5180 UINT rc;
5182 package->script->InWhatSequence |= SEQUENCE_EXEC;
5183 rc = ACTION_ProcessExecSequence(package,FALSE);
5184 return rc;
5187 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5189 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5190 WCHAR productid_85[21], component_85[21], *ret;
5191 GUID clsid;
5192 DWORD sz;
5194 /* > is used if there is a component GUID and < if not. */
5196 productid_85[0] = 0;
5197 component_85[0] = 0;
5198 CLSIDFromString( package->ProductCode, &clsid );
5200 encode_base85_guid( &clsid, productid_85 );
5201 if (component)
5203 CLSIDFromString( component->ComponentId, &clsid );
5204 encode_base85_guid( &clsid, component_85 );
5207 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5208 debugstr_w(component_85));
5210 sz = 20 + strlenW( feature ) + 20 + 3;
5211 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5212 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5213 return ret;
5216 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5218 MSIPACKAGE *package = param;
5219 LPCWSTR compgroupid, component, feature, qualifier, text;
5220 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5221 HKEY hkey = NULL;
5222 UINT rc;
5223 MSICOMPONENT *comp;
5224 MSIFEATURE *feat;
5225 DWORD sz;
5226 MSIRECORD *uirow;
5227 int len;
5229 feature = MSI_RecordGetString(rec, 5);
5230 feat = msi_get_loaded_feature(package, feature);
5231 if (!feat)
5232 return ERROR_SUCCESS;
5234 feat->Action = msi_get_feature_action( package, feat );
5235 if (feat->Action != INSTALLSTATE_LOCAL &&
5236 feat->Action != INSTALLSTATE_SOURCE &&
5237 feat->Action != INSTALLSTATE_ADVERTISED)
5239 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5240 return ERROR_SUCCESS;
5243 component = MSI_RecordGetString(rec, 3);
5244 comp = msi_get_loaded_component(package, component);
5245 if (!comp)
5246 return ERROR_SUCCESS;
5248 compgroupid = MSI_RecordGetString(rec,1);
5249 qualifier = MSI_RecordGetString(rec,2);
5251 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5252 if (rc != ERROR_SUCCESS)
5253 goto end;
5255 advertise = msi_create_component_advertise_string( package, comp, feature );
5256 text = MSI_RecordGetString( rec, 4 );
5257 if (text)
5259 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5260 strcpyW( p, advertise );
5261 strcatW( p, text );
5262 msi_free( advertise );
5263 advertise = p;
5265 existing = msi_reg_get_val_str( hkey, qualifier );
5267 sz = strlenW( advertise ) + 1;
5268 if (existing)
5270 for (p = existing; *p; p += len)
5272 len = strlenW( p ) + 1;
5273 if (strcmpW( advertise, p )) sz += len;
5276 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5278 rc = ERROR_OUTOFMEMORY;
5279 goto end;
5281 q = output;
5282 if (existing)
5284 for (p = existing; *p; p += len)
5286 len = strlenW( p ) + 1;
5287 if (strcmpW( advertise, p ))
5289 memcpy( q, p, len * sizeof(WCHAR) );
5290 q += len;
5294 strcpyW( q, advertise );
5295 q[strlenW( q ) + 1] = 0;
5297 msi_reg_set_val_multi_str( hkey, qualifier, output );
5299 end:
5300 RegCloseKey(hkey);
5301 msi_free( output );
5302 msi_free( advertise );
5303 msi_free( existing );
5305 /* the UI chunk */
5306 uirow = MSI_CreateRecord( 2 );
5307 MSI_RecordSetStringW( uirow, 1, compgroupid );
5308 MSI_RecordSetStringW( uirow, 2, qualifier);
5309 msi_ui_actiondata( package, szPublishComponents, uirow );
5310 msiobj_release( &uirow->hdr );
5311 /* FIXME: call ui_progress? */
5313 return rc;
5317 * At present I am ignorning the advertised components part of this and only
5318 * focusing on the qualified component sets
5320 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5322 static const WCHAR query[] = {
5323 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5324 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5325 MSIQUERY *view;
5326 UINT rc;
5328 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5329 if (rc != ERROR_SUCCESS)
5330 return ERROR_SUCCESS;
5332 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5333 msiobj_release(&view->hdr);
5334 return rc;
5337 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5339 static const WCHAR szInstallerComponents[] = {
5340 'S','o','f','t','w','a','r','e','\\',
5341 'M','i','c','r','o','s','o','f','t','\\',
5342 'I','n','s','t','a','l','l','e','r','\\',
5343 'C','o','m','p','o','n','e','n','t','s','\\',0};
5345 MSIPACKAGE *package = param;
5346 LPCWSTR compgroupid, component, feature, qualifier;
5347 MSICOMPONENT *comp;
5348 MSIFEATURE *feat;
5349 MSIRECORD *uirow;
5350 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5351 LONG res;
5353 feature = MSI_RecordGetString( rec, 5 );
5354 feat = msi_get_loaded_feature( package, feature );
5355 if (!feat)
5356 return ERROR_SUCCESS;
5358 feat->Action = msi_get_feature_action( package, feat );
5359 if (feat->Action != INSTALLSTATE_ABSENT)
5361 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5362 return ERROR_SUCCESS;
5365 component = MSI_RecordGetString( rec, 3 );
5366 comp = msi_get_loaded_component( package, component );
5367 if (!comp)
5368 return ERROR_SUCCESS;
5370 compgroupid = MSI_RecordGetString( rec, 1 );
5371 qualifier = MSI_RecordGetString( rec, 2 );
5373 squash_guid( compgroupid, squashed );
5374 strcpyW( keypath, szInstallerComponents );
5375 strcatW( keypath, squashed );
5377 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5378 if (res != ERROR_SUCCESS)
5380 WARN("Unable to delete component key %d\n", res);
5383 uirow = MSI_CreateRecord( 2 );
5384 MSI_RecordSetStringW( uirow, 1, compgroupid );
5385 MSI_RecordSetStringW( uirow, 2, qualifier );
5386 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5387 msiobj_release( &uirow->hdr );
5389 return ERROR_SUCCESS;
5392 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5394 static const WCHAR query[] = {
5395 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5396 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5397 MSIQUERY *view;
5398 UINT rc;
5400 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5401 if (rc != ERROR_SUCCESS)
5402 return ERROR_SUCCESS;
5404 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5405 msiobj_release( &view->hdr );
5406 return rc;
5409 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5411 static const WCHAR query[] =
5412 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5413 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5414 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5415 MSIPACKAGE *package = param;
5416 MSICOMPONENT *component;
5417 MSIRECORD *row;
5418 MSIFILE *file;
5419 SC_HANDLE hscm = NULL, service = NULL;
5420 LPCWSTR comp, key;
5421 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5422 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5423 DWORD serv_type, start_type, err_control;
5424 SERVICE_DESCRIPTIONW sd = {NULL};
5426 comp = MSI_RecordGetString( rec, 12 );
5427 component = msi_get_loaded_component( package, comp );
5428 if (!component)
5430 WARN("service component not found\n");
5431 goto done;
5433 component->Action = msi_get_component_action( package, component );
5434 if (component->Action != INSTALLSTATE_LOCAL)
5436 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5437 goto done;
5439 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5440 if (!hscm)
5442 ERR("Failed to open the SC Manager!\n");
5443 goto done;
5446 start_type = MSI_RecordGetInteger(rec, 5);
5447 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5448 goto done;
5450 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5451 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5452 serv_type = MSI_RecordGetInteger(rec, 4);
5453 err_control = MSI_RecordGetInteger(rec, 6);
5454 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5455 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5456 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5457 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5458 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5459 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5461 /* fetch the service path */
5462 row = MSI_QueryGetRecord(package->db, query, comp);
5463 if (!row)
5465 ERR("Query failed\n");
5466 goto done;
5468 key = MSI_RecordGetString(row, 6);
5469 file = msi_get_loaded_file(package, key);
5470 msiobj_release(&row->hdr);
5471 if (!file)
5473 ERR("Failed to load the service file\n");
5474 goto done;
5477 if (!args || !args[0]) image_path = file->TargetPath;
5478 else
5480 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5481 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5482 return ERROR_OUTOFMEMORY;
5484 strcpyW(image_path, file->TargetPath);
5485 strcatW(image_path, szSpace);
5486 strcatW(image_path, args);
5488 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5489 start_type, err_control, image_path, load_order,
5490 NULL, depends, serv_name, pass);
5492 if (!service)
5494 if (GetLastError() != ERROR_SERVICE_EXISTS)
5495 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5497 else if (sd.lpDescription)
5499 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5500 WARN("failed to set service description %u\n", GetLastError());
5503 if (image_path != file->TargetPath) msi_free(image_path);
5504 done:
5505 CloseServiceHandle(service);
5506 CloseServiceHandle(hscm);
5507 msi_free(name);
5508 msi_free(disp);
5509 msi_free(sd.lpDescription);
5510 msi_free(load_order);
5511 msi_free(serv_name);
5512 msi_free(pass);
5513 msi_free(depends);
5514 msi_free(args);
5516 return ERROR_SUCCESS;
5519 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5521 static const WCHAR query[] = {
5522 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5523 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5524 MSIQUERY *view;
5525 UINT rc;
5527 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5528 if (rc != ERROR_SUCCESS)
5529 return ERROR_SUCCESS;
5531 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5532 msiobj_release(&view->hdr);
5533 return rc;
5536 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5537 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5539 LPCWSTR *vector, *temp_vector;
5540 LPWSTR p, q;
5541 DWORD sep_len;
5543 static const WCHAR separator[] = {'[','~',']',0};
5545 *numargs = 0;
5546 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5548 if (!args)
5549 return NULL;
5551 vector = msi_alloc(sizeof(LPWSTR));
5552 if (!vector)
5553 return NULL;
5555 p = args;
5558 (*numargs)++;
5559 vector[*numargs - 1] = p;
5561 if ((q = strstrW(p, separator)))
5563 *q = '\0';
5565 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5566 if (!temp_vector)
5568 msi_free(vector);
5569 return NULL;
5571 vector = temp_vector;
5573 p = q + sep_len;
5575 } while (q);
5577 return vector;
5580 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5582 MSIPACKAGE *package = param;
5583 MSICOMPONENT *comp;
5584 MSIRECORD *uirow;
5585 SC_HANDLE scm = NULL, service = NULL;
5586 LPCWSTR component, *vector = NULL;
5587 LPWSTR name, args, display_name = NULL;
5588 DWORD event, numargs, len;
5589 UINT r = ERROR_FUNCTION_FAILED;
5591 component = MSI_RecordGetString(rec, 6);
5592 comp = msi_get_loaded_component(package, component);
5593 if (!comp)
5594 return ERROR_SUCCESS;
5596 comp->Action = msi_get_component_action( package, comp );
5597 if (comp->Action != INSTALLSTATE_LOCAL)
5599 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5600 return ERROR_SUCCESS;
5603 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5604 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5605 event = MSI_RecordGetInteger(rec, 3);
5607 if (!(event & msidbServiceControlEventStart))
5609 r = ERROR_SUCCESS;
5610 goto done;
5613 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5614 if (!scm)
5616 ERR("Failed to open the service control manager\n");
5617 goto done;
5620 len = 0;
5621 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5622 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5624 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5625 GetServiceDisplayNameW( scm, name, display_name, &len );
5628 service = OpenServiceW(scm, name, SERVICE_START);
5629 if (!service)
5631 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5632 goto done;
5635 vector = msi_service_args_to_vector(args, &numargs);
5637 if (!StartServiceW(service, numargs, vector) &&
5638 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5640 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5641 goto done;
5644 r = ERROR_SUCCESS;
5646 done:
5647 uirow = MSI_CreateRecord( 2 );
5648 MSI_RecordSetStringW( uirow, 1, display_name );
5649 MSI_RecordSetStringW( uirow, 2, name );
5650 msi_ui_actiondata( package, szStartServices, uirow );
5651 msiobj_release( &uirow->hdr );
5653 CloseServiceHandle(service);
5654 CloseServiceHandle(scm);
5656 msi_free(name);
5657 msi_free(args);
5658 msi_free(vector);
5659 msi_free(display_name);
5660 return r;
5663 static UINT ACTION_StartServices( MSIPACKAGE *package )
5665 static const WCHAR query[] = {
5666 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5667 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5668 MSIQUERY *view;
5669 UINT rc;
5671 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5672 if (rc != ERROR_SUCCESS)
5673 return ERROR_SUCCESS;
5675 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5676 msiobj_release(&view->hdr);
5677 return rc;
5680 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5682 DWORD i, needed, count;
5683 ENUM_SERVICE_STATUSW *dependencies;
5684 SERVICE_STATUS ss;
5685 SC_HANDLE depserv;
5687 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5688 0, &needed, &count))
5689 return TRUE;
5691 if (GetLastError() != ERROR_MORE_DATA)
5692 return FALSE;
5694 dependencies = msi_alloc(needed);
5695 if (!dependencies)
5696 return FALSE;
5698 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5699 needed, &needed, &count))
5700 goto error;
5702 for (i = 0; i < count; i++)
5704 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5705 SERVICE_STOP | SERVICE_QUERY_STATUS);
5706 if (!depserv)
5707 goto error;
5709 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5710 goto error;
5713 return TRUE;
5715 error:
5716 msi_free(dependencies);
5717 return FALSE;
5720 static UINT stop_service( LPCWSTR name )
5722 SC_HANDLE scm = NULL, service = NULL;
5723 SERVICE_STATUS status;
5724 SERVICE_STATUS_PROCESS ssp;
5725 DWORD needed;
5727 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5728 if (!scm)
5730 WARN("Failed to open the SCM: %d\n", GetLastError());
5731 goto done;
5734 service = OpenServiceW(scm, name,
5735 SERVICE_STOP |
5736 SERVICE_QUERY_STATUS |
5737 SERVICE_ENUMERATE_DEPENDENTS);
5738 if (!service)
5740 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5741 goto done;
5744 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5745 sizeof(SERVICE_STATUS_PROCESS), &needed))
5747 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5748 goto done;
5751 if (ssp.dwCurrentState == SERVICE_STOPPED)
5752 goto done;
5754 stop_service_dependents(scm, service);
5756 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5757 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5759 done:
5760 CloseServiceHandle(service);
5761 CloseServiceHandle(scm);
5763 return ERROR_SUCCESS;
5766 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5768 MSIPACKAGE *package = param;
5769 MSICOMPONENT *comp;
5770 MSIRECORD *uirow;
5771 LPCWSTR component;
5772 LPWSTR name = NULL, display_name = NULL;
5773 DWORD event, len;
5774 SC_HANDLE scm;
5776 event = MSI_RecordGetInteger( rec, 3 );
5777 if (!(event & msidbServiceControlEventStop))
5778 return ERROR_SUCCESS;
5780 component = MSI_RecordGetString( rec, 6 );
5781 comp = msi_get_loaded_component( package, component );
5782 if (!comp)
5783 return ERROR_SUCCESS;
5785 comp->Action = msi_get_component_action( package, comp );
5786 if (comp->Action != INSTALLSTATE_ABSENT)
5788 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5789 return ERROR_SUCCESS;
5792 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5793 if (!scm)
5795 ERR("Failed to open the service control manager\n");
5796 goto done;
5799 len = 0;
5800 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5801 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5803 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5804 GetServiceDisplayNameW( scm, name, display_name, &len );
5806 CloseServiceHandle( scm );
5808 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5809 stop_service( name );
5811 done:
5812 uirow = MSI_CreateRecord( 2 );
5813 MSI_RecordSetStringW( uirow, 1, display_name );
5814 MSI_RecordSetStringW( uirow, 2, name );
5815 msi_ui_actiondata( package, szStopServices, uirow );
5816 msiobj_release( &uirow->hdr );
5818 msi_free( name );
5819 msi_free( display_name );
5820 return ERROR_SUCCESS;
5823 static UINT ACTION_StopServices( MSIPACKAGE *package )
5825 static const WCHAR query[] = {
5826 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5827 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5828 MSIQUERY *view;
5829 UINT rc;
5831 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5832 if (rc != ERROR_SUCCESS)
5833 return ERROR_SUCCESS;
5835 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5836 msiobj_release(&view->hdr);
5837 return rc;
5840 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5842 MSIPACKAGE *package = param;
5843 MSICOMPONENT *comp;
5844 MSIRECORD *uirow;
5845 LPCWSTR component;
5846 LPWSTR name = NULL, display_name = NULL;
5847 DWORD event, len;
5848 SC_HANDLE scm = NULL, service = NULL;
5850 event = MSI_RecordGetInteger( rec, 3 );
5851 if (!(event & msidbServiceControlEventDelete))
5852 return ERROR_SUCCESS;
5854 component = MSI_RecordGetString(rec, 6);
5855 comp = msi_get_loaded_component(package, component);
5856 if (!comp)
5857 return ERROR_SUCCESS;
5859 comp->Action = msi_get_component_action( package, comp );
5860 if (comp->Action != INSTALLSTATE_ABSENT)
5862 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5863 return ERROR_SUCCESS;
5866 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5867 stop_service( name );
5869 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5870 if (!scm)
5872 WARN("Failed to open the SCM: %d\n", GetLastError());
5873 goto done;
5876 len = 0;
5877 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5878 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5880 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5881 GetServiceDisplayNameW( scm, name, display_name, &len );
5884 service = OpenServiceW( scm, name, DELETE );
5885 if (!service)
5887 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5888 goto done;
5891 if (!DeleteService( service ))
5892 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5894 done:
5895 uirow = MSI_CreateRecord( 2 );
5896 MSI_RecordSetStringW( uirow, 1, display_name );
5897 MSI_RecordSetStringW( uirow, 2, name );
5898 msi_ui_actiondata( package, szDeleteServices, uirow );
5899 msiobj_release( &uirow->hdr );
5901 CloseServiceHandle( service );
5902 CloseServiceHandle( scm );
5903 msi_free( name );
5904 msi_free( display_name );
5906 return ERROR_SUCCESS;
5909 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5911 static const WCHAR query[] = {
5912 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5913 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5914 MSIQUERY *view;
5915 UINT rc;
5917 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5918 if (rc != ERROR_SUCCESS)
5919 return ERROR_SUCCESS;
5921 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5922 msiobj_release( &view->hdr );
5923 return rc;
5926 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5928 MSIPACKAGE *package = param;
5929 LPWSTR driver, driver_path, ptr;
5930 WCHAR outpath[MAX_PATH];
5931 MSIFILE *driver_file = NULL, *setup_file = NULL;
5932 MSICOMPONENT *comp;
5933 MSIRECORD *uirow;
5934 LPCWSTR desc, file_key, component;
5935 DWORD len, usage;
5936 UINT r = ERROR_SUCCESS;
5938 static const WCHAR driver_fmt[] = {
5939 'D','r','i','v','e','r','=','%','s',0};
5940 static const WCHAR setup_fmt[] = {
5941 'S','e','t','u','p','=','%','s',0};
5942 static const WCHAR usage_fmt[] = {
5943 'F','i','l','e','U','s','a','g','e','=','1',0};
5945 component = MSI_RecordGetString( rec, 2 );
5946 comp = msi_get_loaded_component( package, component );
5947 if (!comp)
5948 return ERROR_SUCCESS;
5950 comp->Action = msi_get_component_action( package, comp );
5951 if (comp->Action != INSTALLSTATE_LOCAL)
5953 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5954 return ERROR_SUCCESS;
5956 desc = MSI_RecordGetString(rec, 3);
5958 file_key = MSI_RecordGetString( rec, 4 );
5959 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
5961 file_key = MSI_RecordGetString( rec, 5 );
5962 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
5964 if (!driver_file)
5966 ERR("ODBC Driver entry not found!\n");
5967 return ERROR_FUNCTION_FAILED;
5970 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
5971 if (setup_file)
5972 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
5973 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
5975 driver = msi_alloc(len * sizeof(WCHAR));
5976 if (!driver)
5977 return ERROR_OUTOFMEMORY;
5979 ptr = driver;
5980 lstrcpyW(ptr, desc);
5981 ptr += lstrlenW(ptr) + 1;
5983 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
5984 ptr += len + 1;
5986 if (setup_file)
5988 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
5989 ptr += len + 1;
5992 lstrcpyW(ptr, usage_fmt);
5993 ptr += lstrlenW(ptr) + 1;
5994 *ptr = '\0';
5996 driver_path = strdupW(driver_file->TargetPath);
5997 ptr = strrchrW(driver_path, '\\');
5998 if (ptr) *ptr = '\0';
6000 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6001 NULL, ODBC_INSTALL_COMPLETE, &usage))
6003 ERR("Failed to install SQL driver!\n");
6004 r = ERROR_FUNCTION_FAILED;
6007 uirow = MSI_CreateRecord( 5 );
6008 MSI_RecordSetStringW( uirow, 1, desc );
6009 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6010 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6011 msi_ui_actiondata( package, szInstallODBC, uirow );
6012 msiobj_release( &uirow->hdr );
6014 msi_free(driver);
6015 msi_free(driver_path);
6017 return r;
6020 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6022 MSIPACKAGE *package = param;
6023 LPWSTR translator, translator_path, ptr;
6024 WCHAR outpath[MAX_PATH];
6025 MSIFILE *translator_file = NULL, *setup_file = NULL;
6026 MSICOMPONENT *comp;
6027 MSIRECORD *uirow;
6028 LPCWSTR desc, file_key, component;
6029 DWORD len, usage;
6030 UINT r = ERROR_SUCCESS;
6032 static const WCHAR translator_fmt[] = {
6033 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6034 static const WCHAR setup_fmt[] = {
6035 'S','e','t','u','p','=','%','s',0};
6037 component = MSI_RecordGetString( rec, 2 );
6038 comp = msi_get_loaded_component( package, component );
6039 if (!comp)
6040 return ERROR_SUCCESS;
6042 comp->Action = msi_get_component_action( package, comp );
6043 if (comp->Action != INSTALLSTATE_LOCAL)
6045 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6046 return ERROR_SUCCESS;
6048 desc = MSI_RecordGetString(rec, 3);
6050 file_key = MSI_RecordGetString( rec, 4 );
6051 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6053 file_key = MSI_RecordGetString( rec, 5 );
6054 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6056 if (!translator_file)
6058 ERR("ODBC Translator entry not found!\n");
6059 return ERROR_FUNCTION_FAILED;
6062 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6063 if (setup_file)
6064 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6066 translator = msi_alloc(len * sizeof(WCHAR));
6067 if (!translator)
6068 return ERROR_OUTOFMEMORY;
6070 ptr = translator;
6071 lstrcpyW(ptr, desc);
6072 ptr += lstrlenW(ptr) + 1;
6074 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6075 ptr += len + 1;
6077 if (setup_file)
6079 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6080 ptr += len + 1;
6082 *ptr = '\0';
6084 translator_path = strdupW(translator_file->TargetPath);
6085 ptr = strrchrW(translator_path, '\\');
6086 if (ptr) *ptr = '\0';
6088 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6089 NULL, ODBC_INSTALL_COMPLETE, &usage))
6091 ERR("Failed to install SQL translator!\n");
6092 r = ERROR_FUNCTION_FAILED;
6095 uirow = MSI_CreateRecord( 5 );
6096 MSI_RecordSetStringW( uirow, 1, desc );
6097 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6098 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6099 msi_ui_actiondata( package, szInstallODBC, uirow );
6100 msiobj_release( &uirow->hdr );
6102 msi_free(translator);
6103 msi_free(translator_path);
6105 return r;
6108 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6110 MSIPACKAGE *package = param;
6111 MSICOMPONENT *comp;
6112 LPWSTR attrs;
6113 LPCWSTR desc, driver, component;
6114 WORD request = ODBC_ADD_SYS_DSN;
6115 INT registration;
6116 DWORD len;
6117 UINT r = ERROR_SUCCESS;
6118 MSIRECORD *uirow;
6120 static const WCHAR attrs_fmt[] = {
6121 'D','S','N','=','%','s',0 };
6123 component = MSI_RecordGetString( rec, 2 );
6124 comp = msi_get_loaded_component( package, component );
6125 if (!comp)
6126 return ERROR_SUCCESS;
6128 comp->Action = msi_get_component_action( package, comp );
6129 if (comp->Action != INSTALLSTATE_LOCAL)
6131 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6132 return ERROR_SUCCESS;
6135 desc = MSI_RecordGetString(rec, 3);
6136 driver = MSI_RecordGetString(rec, 4);
6137 registration = MSI_RecordGetInteger(rec, 5);
6139 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6140 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6142 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6143 attrs = msi_alloc(len * sizeof(WCHAR));
6144 if (!attrs)
6145 return ERROR_OUTOFMEMORY;
6147 len = sprintfW(attrs, attrs_fmt, desc);
6148 attrs[len + 1] = 0;
6150 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6152 ERR("Failed to install SQL data source!\n");
6153 r = ERROR_FUNCTION_FAILED;
6156 uirow = MSI_CreateRecord( 5 );
6157 MSI_RecordSetStringW( uirow, 1, desc );
6158 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6159 MSI_RecordSetInteger( uirow, 3, request );
6160 msi_ui_actiondata( package, szInstallODBC, uirow );
6161 msiobj_release( &uirow->hdr );
6163 msi_free(attrs);
6165 return r;
6168 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6170 static const WCHAR driver_query[] = {
6171 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6172 'O','D','B','C','D','r','i','v','e','r',0};
6173 static const WCHAR translator_query[] = {
6174 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6175 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6176 static const WCHAR source_query[] = {
6177 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6178 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6179 MSIQUERY *view;
6180 UINT rc;
6182 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6183 if (rc != ERROR_SUCCESS)
6184 return ERROR_SUCCESS;
6186 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6187 msiobj_release(&view->hdr);
6189 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6190 if (rc != ERROR_SUCCESS)
6191 return ERROR_SUCCESS;
6193 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6194 msiobj_release(&view->hdr);
6196 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6197 if (rc != ERROR_SUCCESS)
6198 return ERROR_SUCCESS;
6200 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6201 msiobj_release(&view->hdr);
6202 return rc;
6205 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6207 MSIPACKAGE *package = param;
6208 MSICOMPONENT *comp;
6209 MSIRECORD *uirow;
6210 DWORD usage;
6211 LPCWSTR desc, component;
6213 component = MSI_RecordGetString( rec, 2 );
6214 comp = msi_get_loaded_component( package, component );
6215 if (!comp)
6216 return ERROR_SUCCESS;
6218 comp->Action = msi_get_component_action( package, comp );
6219 if (comp->Action != INSTALLSTATE_ABSENT)
6221 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6222 return ERROR_SUCCESS;
6225 desc = MSI_RecordGetString( rec, 3 );
6226 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6228 WARN("Failed to remove ODBC driver\n");
6230 else if (!usage)
6232 FIXME("Usage count reached 0\n");
6235 uirow = MSI_CreateRecord( 2 );
6236 MSI_RecordSetStringW( uirow, 1, desc );
6237 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6238 msi_ui_actiondata( package, szRemoveODBC, uirow );
6239 msiobj_release( &uirow->hdr );
6241 return ERROR_SUCCESS;
6244 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6246 MSIPACKAGE *package = param;
6247 MSICOMPONENT *comp;
6248 MSIRECORD *uirow;
6249 DWORD usage;
6250 LPCWSTR desc, component;
6252 component = MSI_RecordGetString( rec, 2 );
6253 comp = msi_get_loaded_component( package, component );
6254 if (!comp)
6255 return ERROR_SUCCESS;
6257 comp->Action = msi_get_component_action( package, comp );
6258 if (comp->Action != INSTALLSTATE_ABSENT)
6260 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6261 return ERROR_SUCCESS;
6264 desc = MSI_RecordGetString( rec, 3 );
6265 if (!SQLRemoveTranslatorW( desc, &usage ))
6267 WARN("Failed to remove ODBC translator\n");
6269 else if (!usage)
6271 FIXME("Usage count reached 0\n");
6274 uirow = MSI_CreateRecord( 2 );
6275 MSI_RecordSetStringW( uirow, 1, desc );
6276 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6277 msi_ui_actiondata( package, szRemoveODBC, uirow );
6278 msiobj_release( &uirow->hdr );
6280 return ERROR_SUCCESS;
6283 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6285 MSIPACKAGE *package = param;
6286 MSICOMPONENT *comp;
6287 MSIRECORD *uirow;
6288 LPWSTR attrs;
6289 LPCWSTR desc, driver, component;
6290 WORD request = ODBC_REMOVE_SYS_DSN;
6291 INT registration;
6292 DWORD len;
6294 static const WCHAR attrs_fmt[] = {
6295 'D','S','N','=','%','s',0 };
6297 component = MSI_RecordGetString( rec, 2 );
6298 comp = msi_get_loaded_component( package, component );
6299 if (!comp)
6300 return ERROR_SUCCESS;
6302 comp->Action = msi_get_component_action( package, comp );
6303 if (comp->Action != INSTALLSTATE_ABSENT)
6305 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6306 return ERROR_SUCCESS;
6309 desc = MSI_RecordGetString( rec, 3 );
6310 driver = MSI_RecordGetString( rec, 4 );
6311 registration = MSI_RecordGetInteger( rec, 5 );
6313 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6314 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6316 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6317 attrs = msi_alloc( len * sizeof(WCHAR) );
6318 if (!attrs)
6319 return ERROR_OUTOFMEMORY;
6321 FIXME("Use ODBCSourceAttribute table\n");
6323 len = sprintfW( attrs, attrs_fmt, desc );
6324 attrs[len + 1] = 0;
6326 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6328 WARN("Failed to remove ODBC data source\n");
6330 msi_free( attrs );
6332 uirow = MSI_CreateRecord( 3 );
6333 MSI_RecordSetStringW( uirow, 1, desc );
6334 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6335 MSI_RecordSetInteger( uirow, 3, request );
6336 msi_ui_actiondata( package, szRemoveODBC, uirow );
6337 msiobj_release( &uirow->hdr );
6339 return ERROR_SUCCESS;
6342 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6344 static const WCHAR driver_query[] = {
6345 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6346 'O','D','B','C','D','r','i','v','e','r',0};
6347 static const WCHAR translator_query[] = {
6348 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6349 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6350 static const WCHAR source_query[] = {
6351 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6352 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6353 MSIQUERY *view;
6354 UINT rc;
6356 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6357 if (rc != ERROR_SUCCESS)
6358 return ERROR_SUCCESS;
6360 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6361 msiobj_release( &view->hdr );
6363 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6364 if (rc != ERROR_SUCCESS)
6365 return ERROR_SUCCESS;
6367 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6368 msiobj_release( &view->hdr );
6370 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6371 if (rc != ERROR_SUCCESS)
6372 return ERROR_SUCCESS;
6374 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6375 msiobj_release( &view->hdr );
6376 return rc;
6379 #define ENV_ACT_SETALWAYS 0x1
6380 #define ENV_ACT_SETABSENT 0x2
6381 #define ENV_ACT_REMOVE 0x4
6382 #define ENV_ACT_REMOVEMATCH 0x8
6384 #define ENV_MOD_MACHINE 0x20000000
6385 #define ENV_MOD_APPEND 0x40000000
6386 #define ENV_MOD_PREFIX 0x80000000
6387 #define ENV_MOD_MASK 0xC0000000
6389 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6391 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6393 LPCWSTR cptr = *name;
6395 static const WCHAR prefix[] = {'[','~',']',0};
6396 static const int prefix_len = 3;
6398 *flags = 0;
6399 while (*cptr)
6401 if (*cptr == '=')
6402 *flags |= ENV_ACT_SETALWAYS;
6403 else if (*cptr == '+')
6404 *flags |= ENV_ACT_SETABSENT;
6405 else if (*cptr == '-')
6406 *flags |= ENV_ACT_REMOVE;
6407 else if (*cptr == '!')
6408 *flags |= ENV_ACT_REMOVEMATCH;
6409 else if (*cptr == '*')
6410 *flags |= ENV_MOD_MACHINE;
6411 else
6412 break;
6414 cptr++;
6415 (*name)++;
6418 if (!*cptr)
6420 ERR("Missing environment variable\n");
6421 return ERROR_FUNCTION_FAILED;
6424 if (*value)
6426 LPCWSTR ptr = *value;
6427 if (!strncmpW(ptr, prefix, prefix_len))
6429 if (ptr[prefix_len] == szSemiColon[0])
6431 *flags |= ENV_MOD_APPEND;
6432 *value += lstrlenW(prefix);
6434 else
6436 *value = NULL;
6439 else if (lstrlenW(*value) >= prefix_len)
6441 ptr += lstrlenW(ptr) - prefix_len;
6442 if (!strcmpW( ptr, prefix ))
6444 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6446 *flags |= ENV_MOD_PREFIX;
6447 /* the "[~]" will be removed by deformat_string */;
6449 else
6451 *value = NULL;
6457 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6458 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6459 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6460 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6462 ERR("Invalid flags: %08x\n", *flags);
6463 return ERROR_FUNCTION_FAILED;
6466 if (!*flags)
6467 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6469 return ERROR_SUCCESS;
6472 static UINT open_env_key( DWORD flags, HKEY *key )
6474 static const WCHAR user_env[] =
6475 {'E','n','v','i','r','o','n','m','e','n','t',0};
6476 static const WCHAR machine_env[] =
6477 {'S','y','s','t','e','m','\\',
6478 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6479 'C','o','n','t','r','o','l','\\',
6480 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6481 'E','n','v','i','r','o','n','m','e','n','t',0};
6482 const WCHAR *env;
6483 HKEY root;
6484 LONG res;
6486 if (flags & ENV_MOD_MACHINE)
6488 env = machine_env;
6489 root = HKEY_LOCAL_MACHINE;
6491 else
6493 env = user_env;
6494 root = HKEY_CURRENT_USER;
6497 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6498 if (res != ERROR_SUCCESS)
6500 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6501 return ERROR_FUNCTION_FAILED;
6504 return ERROR_SUCCESS;
6507 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6509 MSIPACKAGE *package = param;
6510 LPCWSTR name, value, component;
6511 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6512 DWORD flags, type, size;
6513 UINT res;
6514 HKEY env = NULL;
6515 MSICOMPONENT *comp;
6516 MSIRECORD *uirow;
6517 int action = 0;
6519 component = MSI_RecordGetString(rec, 4);
6520 comp = msi_get_loaded_component(package, component);
6521 if (!comp)
6522 return ERROR_SUCCESS;
6524 comp->Action = msi_get_component_action( package, comp );
6525 if (comp->Action != INSTALLSTATE_LOCAL)
6527 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6528 return ERROR_SUCCESS;
6530 name = MSI_RecordGetString(rec, 2);
6531 value = MSI_RecordGetString(rec, 3);
6533 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6535 res = env_parse_flags(&name, &value, &flags);
6536 if (res != ERROR_SUCCESS || !value)
6537 goto done;
6539 if (value && !deformat_string(package, value, &deformatted))
6541 res = ERROR_OUTOFMEMORY;
6542 goto done;
6545 value = deformatted;
6547 res = open_env_key( flags, &env );
6548 if (res != ERROR_SUCCESS)
6549 goto done;
6551 if (flags & ENV_MOD_MACHINE)
6552 action |= 0x20000000;
6554 size = 0;
6555 type = REG_SZ;
6556 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6557 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6558 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6559 goto done;
6561 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6563 action = 0x2;
6565 /* Nothing to do. */
6566 if (!value)
6568 res = ERROR_SUCCESS;
6569 goto done;
6572 /* If we are appending but the string was empty, strip ; */
6573 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6575 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6576 newval = strdupW(value);
6577 if (!newval)
6579 res = ERROR_OUTOFMEMORY;
6580 goto done;
6583 else
6585 action = 0x1;
6587 /* Contrary to MSDN, +-variable to [~];path works */
6588 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6590 res = ERROR_SUCCESS;
6591 goto done;
6594 data = msi_alloc(size);
6595 if (!data)
6597 RegCloseKey(env);
6598 return ERROR_OUTOFMEMORY;
6601 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6602 if (res != ERROR_SUCCESS)
6603 goto done;
6605 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6607 action = 0x4;
6608 res = RegDeleteValueW(env, name);
6609 if (res != ERROR_SUCCESS)
6610 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6611 goto done;
6614 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6615 if (flags & ENV_MOD_MASK)
6617 DWORD mod_size;
6618 int multiplier = 0;
6619 if (flags & ENV_MOD_APPEND) multiplier++;
6620 if (flags & ENV_MOD_PREFIX) multiplier++;
6621 mod_size = lstrlenW(value) * multiplier;
6622 size += mod_size * sizeof(WCHAR);
6625 newval = msi_alloc(size);
6626 ptr = newval;
6627 if (!newval)
6629 res = ERROR_OUTOFMEMORY;
6630 goto done;
6633 if (flags & ENV_MOD_PREFIX)
6635 lstrcpyW(newval, value);
6636 ptr = newval + lstrlenW(value);
6637 action |= 0x80000000;
6640 lstrcpyW(ptr, data);
6642 if (flags & ENV_MOD_APPEND)
6644 lstrcatW(newval, value);
6645 action |= 0x40000000;
6648 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6649 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6650 if (res)
6652 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6655 done:
6656 uirow = MSI_CreateRecord( 3 );
6657 MSI_RecordSetStringW( uirow, 1, name );
6658 MSI_RecordSetStringW( uirow, 2, newval );
6659 MSI_RecordSetInteger( uirow, 3, action );
6660 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6661 msiobj_release( &uirow->hdr );
6663 if (env) RegCloseKey(env);
6664 msi_free(deformatted);
6665 msi_free(data);
6666 msi_free(newval);
6667 return res;
6670 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6672 static const WCHAR query[] = {
6673 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6674 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6675 MSIQUERY *view;
6676 UINT rc;
6678 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6679 if (rc != ERROR_SUCCESS)
6680 return ERROR_SUCCESS;
6682 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6683 msiobj_release(&view->hdr);
6684 return rc;
6687 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6689 MSIPACKAGE *package = param;
6690 LPCWSTR name, value, component;
6691 LPWSTR deformatted = NULL;
6692 DWORD flags;
6693 HKEY env;
6694 MSICOMPONENT *comp;
6695 MSIRECORD *uirow;
6696 int action = 0;
6697 LONG res;
6698 UINT r;
6700 component = MSI_RecordGetString( rec, 4 );
6701 comp = msi_get_loaded_component( package, component );
6702 if (!comp)
6703 return ERROR_SUCCESS;
6705 comp->Action = msi_get_component_action( package, comp );
6706 if (comp->Action != INSTALLSTATE_ABSENT)
6708 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6709 return ERROR_SUCCESS;
6711 name = MSI_RecordGetString( rec, 2 );
6712 value = MSI_RecordGetString( rec, 3 );
6714 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6716 r = env_parse_flags( &name, &value, &flags );
6717 if (r != ERROR_SUCCESS)
6718 return r;
6720 if (!(flags & ENV_ACT_REMOVE))
6722 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6723 return ERROR_SUCCESS;
6726 if (value && !deformat_string( package, value, &deformatted ))
6727 return ERROR_OUTOFMEMORY;
6729 value = deformatted;
6731 r = open_env_key( flags, &env );
6732 if (r != ERROR_SUCCESS)
6734 r = ERROR_SUCCESS;
6735 goto done;
6738 if (flags & ENV_MOD_MACHINE)
6739 action |= 0x20000000;
6741 TRACE("Removing %s\n", debugstr_w(name));
6743 res = RegDeleteValueW( env, name );
6744 if (res != ERROR_SUCCESS)
6746 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6747 r = ERROR_SUCCESS;
6750 done:
6751 uirow = MSI_CreateRecord( 3 );
6752 MSI_RecordSetStringW( uirow, 1, name );
6753 MSI_RecordSetStringW( uirow, 2, value );
6754 MSI_RecordSetInteger( uirow, 3, action );
6755 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6756 msiobj_release( &uirow->hdr );
6758 if (env) RegCloseKey( env );
6759 msi_free( deformatted );
6760 return r;
6763 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6765 static const WCHAR query[] = {
6766 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6767 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6768 MSIQUERY *view;
6769 UINT rc;
6771 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6772 if (rc != ERROR_SUCCESS)
6773 return ERROR_SUCCESS;
6775 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6776 msiobj_release( &view->hdr );
6777 return rc;
6780 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6782 LPWSTR key, template, id;
6783 UINT r = ERROR_SUCCESS;
6785 id = msi_dup_property( package->db, szProductID );
6786 if (id)
6788 msi_free( id );
6789 return ERROR_SUCCESS;
6791 template = msi_dup_property( package->db, szPIDTemplate );
6792 key = msi_dup_property( package->db, szPIDKEY );
6794 if (key && template)
6796 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6797 r = msi_set_property( package->db, szProductID, key );
6799 msi_free( template );
6800 msi_free( key );
6801 return r;
6804 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6806 TRACE("\n");
6807 package->need_reboot = 1;
6808 return ERROR_SUCCESS;
6811 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6813 static const WCHAR szAvailableFreeReg[] =
6814 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6815 MSIRECORD *uirow;
6816 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6818 TRACE("%p %d kilobytes\n", package, space);
6820 uirow = MSI_CreateRecord( 1 );
6821 MSI_RecordSetInteger( uirow, 1, space );
6822 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6823 msiobj_release( &uirow->hdr );
6825 return ERROR_SUCCESS;
6828 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6830 TRACE("%p\n", package);
6832 msi_set_property( package->db, szRollbackDisabled, szOne );
6833 return ERROR_SUCCESS;
6836 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6838 FIXME("%p\n", package);
6839 return ERROR_SUCCESS;
6842 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6844 static const WCHAR driver_query[] = {
6845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6846 'O','D','B','C','D','r','i','v','e','r',0};
6847 static const WCHAR translator_query[] = {
6848 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6849 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6850 MSIQUERY *view;
6851 UINT r, count;
6853 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6854 if (r == ERROR_SUCCESS)
6856 count = 0;
6857 r = MSI_IterateRecords( view, &count, NULL, package );
6858 msiobj_release( &view->hdr );
6859 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6861 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6862 if (r == ERROR_SUCCESS)
6864 count = 0;
6865 r = MSI_IterateRecords( view, &count, NULL, package );
6866 msiobj_release( &view->hdr );
6867 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6869 return ERROR_SUCCESS;
6872 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6874 MSIPACKAGE *package = param;
6875 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6876 WCHAR *value;
6878 if ((value = msi_dup_property( package->db, property )))
6880 FIXME("remove %s\n", debugstr_w(value));
6881 msi_free( value );
6883 return ERROR_SUCCESS;
6886 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6888 static const WCHAR query[] = {
6889 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6890 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6891 MSIQUERY *view;
6892 UINT r;
6894 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6895 if (r == ERROR_SUCCESS)
6897 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6898 msiobj_release( &view->hdr );
6899 return r;
6901 return ERROR_SUCCESS;
6904 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6906 MSIPACKAGE *package = param;
6907 int attributes = MSI_RecordGetInteger( rec, 5 );
6909 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6911 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6912 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6913 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6914 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6915 HKEY hkey;
6916 UINT r;
6918 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
6920 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6921 if (r != ERROR_SUCCESS)
6922 return ERROR_SUCCESS;
6924 else
6926 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6927 if (r != ERROR_SUCCESS)
6928 return ERROR_SUCCESS;
6930 RegCloseKey( hkey );
6932 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
6933 debugstr_w(upgrade_code), debugstr_w(version_min),
6934 debugstr_w(version_max), debugstr_w(language));
6936 return ERROR_SUCCESS;
6939 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6941 static const WCHAR query[] = {
6942 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6943 'U','p','g','r','a','d','e',0};
6944 MSIQUERY *view;
6945 UINT r;
6947 if (msi_get_property_int( package->db, szInstalled, 0 ))
6949 TRACE("product is installed, skipping action\n");
6950 return ERROR_SUCCESS;
6952 if (msi_get_property_int( package->db, szPreselected, 0 ))
6954 TRACE("Preselected property is set, not migrating feature states\n");
6955 return ERROR_SUCCESS;
6957 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6958 if (r == ERROR_SUCCESS)
6960 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
6961 msiobj_release( &view->hdr );
6963 return ERROR_SUCCESS;
6966 static void bind_image( const char *filename, const char *path )
6968 if (!BindImageEx( 0, filename, path, NULL, NULL ))
6970 WARN("failed to bind image %u\n", GetLastError());
6974 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
6976 UINT i;
6977 MSIFILE *file;
6978 MSIPACKAGE *package = param;
6979 const WCHAR *key = MSI_RecordGetString( rec, 1 );
6980 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
6981 char *filenameA, *pathA;
6982 WCHAR *pathW, **path_list;
6984 if (!(file = msi_get_loaded_file( package, key )))
6986 WARN("file %s not found\n", debugstr_w(key));
6987 return ERROR_SUCCESS;
6989 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
6990 path_list = msi_split_string( paths, ';' );
6991 if (!path_list) bind_image( filenameA, NULL );
6992 else
6994 for (i = 0; path_list[i] && path_list[i][0]; i++)
6996 deformat_string( package, path_list[i], &pathW );
6997 if ((pathA = strdupWtoA( pathW )))
6999 bind_image( filenameA, pathA );
7000 msi_free( pathA );
7002 msi_free( pathW );
7005 msi_free( path_list );
7006 msi_free( filenameA );
7007 return ERROR_SUCCESS;
7010 static UINT ACTION_BindImage( MSIPACKAGE *package )
7012 static const WCHAR query[] = {
7013 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7014 'B','i','n','d','I','m','a','g','e',0};
7015 MSIQUERY *view;
7016 UINT r;
7018 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7019 if (r == ERROR_SUCCESS)
7021 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7022 msiobj_release( &view->hdr );
7024 return ERROR_SUCCESS;
7027 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7029 static const WCHAR query[] = {
7030 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7031 MSIQUERY *view;
7032 DWORD count = 0;
7033 UINT r;
7035 r = MSI_OpenQuery( package->db, &view, query, table );
7036 if (r == ERROR_SUCCESS)
7038 r = MSI_IterateRecords(view, &count, NULL, package);
7039 msiobj_release(&view->hdr);
7041 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7042 return ERROR_SUCCESS;
7045 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7047 static const WCHAR table[] = {
7048 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7049 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7052 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7054 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7055 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7058 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7060 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7061 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7064 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7066 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7067 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7070 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7072 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7073 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7076 static const struct
7078 const WCHAR *action;
7079 UINT (*handler)(MSIPACKAGE *);
7080 const WCHAR *action_rollback;
7082 StandardActions[] =
7084 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7085 { szAppSearch, ACTION_AppSearch, NULL },
7086 { szBindImage, ACTION_BindImage, NULL },
7087 { szCCPSearch, ACTION_CCPSearch, NULL },
7088 { szCostFinalize, ACTION_CostFinalize, NULL },
7089 { szCostInitialize, ACTION_CostInitialize, NULL },
7090 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7091 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7092 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7093 { szDisableRollback, ACTION_DisableRollback, NULL },
7094 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7095 { szExecuteAction, ACTION_ExecuteAction, NULL },
7096 { szFileCost, ACTION_FileCost, NULL },
7097 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7098 { szForceReboot, ACTION_ForceReboot, NULL },
7099 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7100 { szInstallExecute, ACTION_InstallExecute, NULL },
7101 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7102 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7103 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7104 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7105 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7106 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7107 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7108 { szInstallValidate, ACTION_InstallValidate, NULL },
7109 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7110 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7111 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7112 { szMoveFiles, ACTION_MoveFiles, NULL },
7113 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7114 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7115 { szPatchFiles, ACTION_PatchFiles, NULL },
7116 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7117 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7118 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7119 { szPublishProduct, ACTION_PublishProduct, NULL },
7120 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7121 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7122 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7123 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7124 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7125 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7126 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7127 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7128 { szRegisterUser, ACTION_RegisterUser, NULL },
7129 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7130 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7131 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7132 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7133 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7134 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7135 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7136 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7137 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7138 { szResolveSource, ACTION_ResolveSource, NULL },
7139 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7140 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7141 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7142 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfUnregModules },
7143 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7144 { szStartServices, ACTION_StartServices, szStopServices },
7145 { szStopServices, ACTION_StopServices, szStartServices },
7146 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7147 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7148 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7149 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7150 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7151 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7152 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7153 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7154 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7155 { szValidateProductID, ACTION_ValidateProductID, NULL },
7156 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7157 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7158 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7159 { NULL, NULL, NULL }
7162 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7164 BOOL ret = FALSE;
7165 UINT i;
7167 i = 0;
7168 while (StandardActions[i].action != NULL)
7170 if (!strcmpW( StandardActions[i].action, action ))
7172 ui_actionstart( package, action );
7173 if (StandardActions[i].handler)
7175 ui_actioninfo( package, action, TRUE, 0 );
7176 *rc = StandardActions[i].handler( package );
7177 ui_actioninfo( package, action, FALSE, *rc );
7179 if (StandardActions[i].action_rollback && !package->need_rollback)
7181 TRACE("scheduling rollback action\n");
7182 msi_schedule_action( package, ROLLBACK_SCRIPT, StandardActions[i].action_rollback );
7185 else
7187 FIXME("unhandled standard action %s\n", debugstr_w(action));
7188 *rc = ERROR_SUCCESS;
7190 ret = TRUE;
7191 break;
7193 i++;
7195 return ret;
7198 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7200 UINT rc = ERROR_SUCCESS;
7201 BOOL handled;
7203 TRACE("Performing action (%s)\n", debugstr_w(action));
7205 handled = ACTION_HandleStandardAction(package, action, &rc);
7207 if (!handled)
7208 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7210 if (!handled)
7212 WARN("unhandled msi action %s\n", debugstr_w(action));
7213 rc = ERROR_FUNCTION_NOT_CALLED;
7216 return rc;
7219 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7221 UINT rc = ERROR_SUCCESS;
7222 BOOL handled = FALSE;
7224 TRACE("Performing action (%s)\n", debugstr_w(action));
7226 handled = ACTION_HandleStandardAction(package, action, &rc);
7228 if (!handled)
7229 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7231 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7232 handled = TRUE;
7234 if (!handled)
7236 WARN("unhandled msi action %s\n", debugstr_w(action));
7237 rc = ERROR_FUNCTION_NOT_CALLED;
7240 return rc;
7243 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7245 UINT rc = ERROR_SUCCESS;
7246 MSIRECORD *row;
7248 static const WCHAR query[] =
7249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7250 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7251 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7252 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7253 static const WCHAR ui_query[] =
7254 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7255 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7256 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7257 ' ', '=',' ','%','i',0};
7259 if (needs_ui_sequence(package))
7260 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7261 else
7262 row = MSI_QueryGetRecord(package->db, query, seq);
7264 if (row)
7266 LPCWSTR action, cond;
7268 TRACE("Running the actions\n");
7270 /* check conditions */
7271 cond = MSI_RecordGetString(row, 2);
7273 /* this is a hack to skip errors in the condition code */
7274 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7276 msiobj_release(&row->hdr);
7277 return ERROR_SUCCESS;
7280 action = MSI_RecordGetString(row, 1);
7281 if (!action)
7283 ERR("failed to fetch action\n");
7284 msiobj_release(&row->hdr);
7285 return ERROR_FUNCTION_FAILED;
7288 if (needs_ui_sequence(package))
7289 rc = ACTION_PerformUIAction(package, action, -1);
7290 else
7291 rc = ACTION_PerformAction(package, action, -1);
7293 msiobj_release(&row->hdr);
7296 return rc;
7299 /****************************************************
7300 * TOP level entry points
7301 *****************************************************/
7303 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7304 LPCWSTR szCommandLine )
7306 UINT rc;
7307 BOOL ui_exists;
7308 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7309 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7310 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7312 msi_set_property( package->db, szAction, szInstall );
7314 package->script->InWhatSequence = SEQUENCE_INSTALL;
7316 if (szPackagePath)
7318 LPWSTR p, dir;
7319 LPCWSTR file;
7321 dir = strdupW(szPackagePath);
7322 p = strrchrW(dir, '\\');
7323 if (p)
7325 *(++p) = 0;
7326 file = szPackagePath + (p - dir);
7328 else
7330 msi_free(dir);
7331 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7332 GetCurrentDirectoryW(MAX_PATH, dir);
7333 lstrcatW(dir, szBackSlash);
7334 file = szPackagePath;
7337 msi_free( package->PackagePath );
7338 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7339 if (!package->PackagePath)
7341 msi_free(dir);
7342 return ERROR_OUTOFMEMORY;
7345 lstrcpyW(package->PackagePath, dir);
7346 lstrcatW(package->PackagePath, file);
7347 msi_free(dir);
7349 msi_set_sourcedir_props(package, FALSE);
7352 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7353 if (rc != ERROR_SUCCESS)
7354 return rc;
7356 msi_apply_transforms( package );
7357 msi_apply_patches( package );
7359 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7361 TRACE("setting reinstall property\n");
7362 msi_set_property( package->db, szReinstall, szAll );
7365 /* properties may have been added by a transform */
7366 msi_clone_properties( package );
7368 msi_parse_command_line( package, szCommandLine, FALSE );
7369 msi_adjust_privilege_properties( package );
7370 msi_set_context( package );
7372 if (msi_get_property_int( package->db, szInstalled, 0 ))
7374 HKEY hkey;
7375 DeleteFileW( package->localfile );
7376 msi_free( package->localfile );
7377 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
7378 package->localfile = msi_reg_get_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW );
7379 RegCloseKey( hkey );
7381 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7383 TRACE("disabling rollback\n");
7384 msi_set_property( package->db, szRollbackDisabled, szOne );
7387 if (needs_ui_sequence( package))
7389 package->script->InWhatSequence |= SEQUENCE_UI;
7390 rc = ACTION_ProcessUISequence(package);
7391 ui_exists = ui_sequence_exists(package);
7392 if (rc == ERROR_SUCCESS || !ui_exists)
7394 package->script->InWhatSequence |= SEQUENCE_EXEC;
7395 rc = ACTION_ProcessExecSequence(package, ui_exists);
7398 else
7399 rc = ACTION_ProcessExecSequence(package, FALSE);
7401 package->script->CurrentlyScripting = FALSE;
7403 /* process the ending type action */
7404 if (rc == ERROR_SUCCESS)
7405 ACTION_PerformActionSequence(package, -1);
7406 else if (rc == ERROR_INSTALL_USEREXIT)
7407 ACTION_PerformActionSequence(package, -2);
7408 else if (rc == ERROR_INSTALL_SUSPEND)
7409 ACTION_PerformActionSequence(package, -4);
7410 else /* failed */
7412 ACTION_PerformActionSequence(package, -3);
7413 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7415 package->need_rollback = TRUE;
7419 /* finish up running custom actions */
7420 ACTION_FinishCustomActions(package);
7422 if (package->need_rollback)
7424 WARN("installation failed, running rollback script\n");
7425 execute_script( package, ROLLBACK_SCRIPT );
7428 if (rc == ERROR_SUCCESS && package->need_reboot)
7429 return ERROR_SUCCESS_REBOOT_REQUIRED;
7431 return rc;