msxml3: Block ::add() if collection is read-only.
[wine/multimedia.git] / dlls / msi / action.c
blob59e7b6d4c3df8355063df2fd9d3e9e08c0192aa7
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 UINT r = msi_locate_product( package->ProductCode, &package->Context );
492 if (r != ERROR_SUCCESS)
494 int num = msi_get_property_int( package->db, szAllUsers, 0 );
495 if (num == 1 || num == 2)
496 package->Context = MSIINSTALLCONTEXT_MACHINE;
497 else
498 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
500 return ERROR_SUCCESS;
503 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
505 UINT rc;
506 LPCWSTR cond, action;
507 MSIPACKAGE *package = param;
509 action = MSI_RecordGetString(row,1);
510 if (!action)
512 ERR("Error is retrieving action name\n");
513 return ERROR_FUNCTION_FAILED;
516 /* check conditions */
517 cond = MSI_RecordGetString(row,2);
519 /* this is a hack to skip errors in the condition code */
520 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
522 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
523 return ERROR_SUCCESS;
526 if (needs_ui_sequence(package))
527 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
528 else
529 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
531 msi_dialog_check_messages( NULL );
533 if (package->CurrentInstallState != ERROR_SUCCESS)
534 rc = package->CurrentInstallState;
536 if (rc == ERROR_FUNCTION_NOT_CALLED)
537 rc = ERROR_SUCCESS;
539 if (rc != ERROR_SUCCESS)
540 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
542 return rc;
545 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
547 static const WCHAR query[] = {
548 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
549 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
550 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
551 '`','S','e','q','u','e','n','c','e','`',0};
552 MSIQUERY *view;
553 UINT r;
555 TRACE("%p %s\n", package, debugstr_w(table));
557 r = MSI_OpenQuery( package->db, &view, query, table );
558 if (r == ERROR_SUCCESS)
560 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
561 msiobj_release(&view->hdr);
563 return r;
566 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
568 static const WCHAR query[] = {
569 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
570 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
571 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
572 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
573 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
574 static const WCHAR query_validate[] = {
575 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
576 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
577 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
578 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
579 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
580 MSIQUERY *view;
581 INT seq = 0;
582 UINT rc;
584 if (package->script->ExecuteSequenceRun)
586 TRACE("Execute Sequence already Run\n");
587 return ERROR_SUCCESS;
590 package->script->ExecuteSequenceRun = TRUE;
592 /* get the sequence number */
593 if (UIran)
595 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
596 if (!row) return ERROR_FUNCTION_FAILED;
597 seq = MSI_RecordGetInteger(row,1);
598 msiobj_release(&row->hdr);
600 rc = MSI_OpenQuery(package->db, &view, query, seq);
601 if (rc == ERROR_SUCCESS)
603 TRACE("Running the actions\n");
605 msi_set_property(package->db, szSourceDir, NULL);
606 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
607 msiobj_release(&view->hdr);
609 return rc;
612 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
614 static const WCHAR query[] = {
615 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
616 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
617 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
618 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
619 MSIQUERY *view;
620 UINT rc;
622 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
623 if (rc == ERROR_SUCCESS)
625 TRACE("Running the actions\n");
626 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
627 msiobj_release(&view->hdr);
629 return rc;
632 /********************************************************
633 * ACTION helper functions and functions that perform the actions
634 *******************************************************/
635 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
636 UINT* rc, UINT script, BOOL force )
638 BOOL ret=FALSE;
639 UINT arc;
641 arc = ACTION_CustomAction(package, action, script, force);
643 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
645 *rc = arc;
646 ret = TRUE;
648 return ret;
651 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
653 MSICOMPONENT *comp;
655 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
657 if (!strcmpW( Component, comp->Component )) return comp;
659 return NULL;
662 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
664 MSIFEATURE *feature;
666 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
668 if (!strcmpW( Feature, feature->Feature )) return feature;
670 return NULL;
673 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
675 MSIFILE *file;
677 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
679 if (!strcmpW( key, file->File )) return file;
681 return NULL;
684 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
686 MSIFILEPATCH *patch;
688 /* FIXME: There might be more than one patch */
689 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
691 if (!strcmpW( key, patch->File->File )) return patch;
693 return NULL;
696 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
698 MSIFOLDER *folder;
700 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
702 if (!strcmpW( dir, folder->Directory )) return folder;
704 return NULL;
708 * Recursively create all directories in the path.
709 * shamelessly stolen from setupapi/queue.c
711 BOOL msi_create_full_path( const WCHAR *path )
713 BOOL ret = TRUE;
714 WCHAR *new_path;
715 int len;
717 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
718 strcpyW( new_path, path );
720 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
721 new_path[len - 1] = 0;
723 while (!CreateDirectoryW( new_path, NULL ))
725 WCHAR *slash;
726 DWORD last_error = GetLastError();
727 if (last_error == ERROR_ALREADY_EXISTS) break;
728 if (last_error != ERROR_PATH_NOT_FOUND)
730 ret = FALSE;
731 break;
733 if (!(slash = strrchrW( new_path, '\\' )))
735 ret = FALSE;
736 break;
738 len = slash - new_path;
739 new_path[len] = 0;
740 if (!msi_create_full_path( new_path ))
742 ret = FALSE;
743 break;
745 new_path[len] = '\\';
747 msi_free( new_path );
748 return ret;
751 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
753 MSIRECORD *row;
755 row = MSI_CreateRecord( 4 );
756 MSI_RecordSetInteger( row, 1, a );
757 MSI_RecordSetInteger( row, 2, b );
758 MSI_RecordSetInteger( row, 3, c );
759 MSI_RecordSetInteger( row, 4, d );
760 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
761 msiobj_release( &row->hdr );
763 msi_dialog_check_messages( NULL );
766 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
768 static const WCHAR query[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
770 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
771 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
772 WCHAR message[1024];
773 MSIRECORD *row = 0;
774 DWORD size;
776 if (!package->LastAction || strcmpW( package->LastAction, action ))
778 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
780 if (MSI_RecordIsNull( row, 3 ))
782 msiobj_release( &row->hdr );
783 return;
785 /* update the cached action format */
786 msi_free( package->ActionFormat );
787 package->ActionFormat = msi_dup_record_field( row, 3 );
788 msi_free( package->LastAction );
789 package->LastAction = strdupW( action );
790 msiobj_release( &row->hdr );
792 size = 1024;
793 MSI_RecordSetStringW( record, 0, package->ActionFormat );
794 MSI_FormatRecordW( package, record, message, &size );
795 row = MSI_CreateRecord( 1 );
796 MSI_RecordSetStringW( row, 1, message );
797 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
798 msiobj_release( &row->hdr );
801 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
803 if (!comp->Enabled)
805 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
806 return INSTALLSTATE_UNKNOWN;
808 if (package->need_rollback) return comp->Installed;
809 return comp->ActionRequest;
812 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
814 if (package->need_rollback) return feature->Installed;
815 return feature->ActionRequest;
818 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
820 MSIPACKAGE *package = param;
821 LPCWSTR dir, component, full_path;
822 MSIRECORD *uirow;
823 MSIFOLDER *folder;
824 MSICOMPONENT *comp;
826 component = MSI_RecordGetString(row, 2);
827 if (!component)
828 return ERROR_SUCCESS;
830 comp = msi_get_loaded_component(package, component);
831 if (!comp)
832 return ERROR_SUCCESS;
834 comp->Action = msi_get_component_action( package, comp );
835 if (comp->Action != INSTALLSTATE_LOCAL)
837 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
838 return ERROR_SUCCESS;
841 dir = MSI_RecordGetString(row,1);
842 if (!dir)
844 ERR("Unable to get folder id\n");
845 return ERROR_SUCCESS;
848 uirow = MSI_CreateRecord(1);
849 MSI_RecordSetStringW(uirow, 1, dir);
850 msi_ui_actiondata(package, szCreateFolders, uirow);
851 msiobj_release(&uirow->hdr);
853 full_path = msi_get_target_folder( package, dir );
854 if (!full_path)
856 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
857 return ERROR_SUCCESS;
859 TRACE("folder is %s\n", debugstr_w(full_path));
861 folder = msi_get_loaded_folder( package, dir );
862 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
863 folder->State = FOLDER_STATE_CREATED;
864 return ERROR_SUCCESS;
867 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
869 static const WCHAR query[] = {
870 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
871 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
872 MSIQUERY *view;
873 UINT rc;
875 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
876 if (rc != ERROR_SUCCESS)
877 return ERROR_SUCCESS;
879 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
880 msiobj_release(&view->hdr);
881 return rc;
884 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
886 MSIPACKAGE *package = param;
887 LPCWSTR dir, component, full_path;
888 MSIRECORD *uirow;
889 MSIFOLDER *folder;
890 MSICOMPONENT *comp;
892 component = MSI_RecordGetString(row, 2);
893 if (!component)
894 return ERROR_SUCCESS;
896 comp = msi_get_loaded_component(package, component);
897 if (!comp)
898 return ERROR_SUCCESS;
900 comp->Action = msi_get_component_action( package, comp );
901 if (comp->Action != INSTALLSTATE_ABSENT)
903 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
904 return ERROR_SUCCESS;
907 dir = MSI_RecordGetString( row, 1 );
908 if (!dir)
910 ERR("Unable to get folder id\n");
911 return ERROR_SUCCESS;
914 full_path = msi_get_target_folder( package, dir );
915 if (!full_path)
917 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
918 return ERROR_SUCCESS;
920 TRACE("folder is %s\n", debugstr_w(full_path));
922 uirow = MSI_CreateRecord( 1 );
923 MSI_RecordSetStringW( uirow, 1, dir );
924 msi_ui_actiondata( package, szRemoveFolders, uirow );
925 msiobj_release( &uirow->hdr );
927 RemoveDirectoryW( full_path );
928 folder = msi_get_loaded_folder( package, dir );
929 folder->State = FOLDER_STATE_REMOVED;
930 return ERROR_SUCCESS;
933 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
935 static const WCHAR query[] = {
936 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
937 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
938 MSIQUERY *view;
939 UINT rc;
941 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
942 if (rc != ERROR_SUCCESS)
943 return ERROR_SUCCESS;
945 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
946 msiobj_release( &view->hdr );
947 return rc;
950 static UINT load_component( MSIRECORD *row, LPVOID param )
952 MSIPACKAGE *package = param;
953 MSICOMPONENT *comp;
955 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
956 if (!comp)
957 return ERROR_FUNCTION_FAILED;
959 list_add_tail( &package->components, &comp->entry );
961 /* fill in the data */
962 comp->Component = msi_dup_record_field( row, 1 );
964 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
966 comp->ComponentId = msi_dup_record_field( row, 2 );
967 comp->Directory = msi_dup_record_field( row, 3 );
968 comp->Attributes = MSI_RecordGetInteger(row,4);
969 comp->Condition = msi_dup_record_field( row, 5 );
970 comp->KeyPath = msi_dup_record_field( row, 6 );
972 comp->Installed = INSTALLSTATE_UNKNOWN;
973 comp->Action = INSTALLSTATE_UNKNOWN;
974 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
976 comp->assembly = msi_load_assembly( package, comp );
977 return ERROR_SUCCESS;
980 UINT msi_load_all_components( MSIPACKAGE *package )
982 static const WCHAR query[] = {
983 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
984 '`','C','o','m','p','o','n','e','n','t','`',0};
985 MSIQUERY *view;
986 UINT r;
988 if (!list_empty(&package->components))
989 return ERROR_SUCCESS;
991 r = MSI_DatabaseOpenViewW( package->db, query, &view );
992 if (r != ERROR_SUCCESS)
993 return r;
995 if (!msi_init_assembly_caches( package ))
997 ERR("can't initialize assembly caches\n");
998 msiobj_release( &view->hdr );
999 return ERROR_FUNCTION_FAILED;
1002 r = MSI_IterateRecords(view, NULL, load_component, package);
1003 msiobj_release(&view->hdr);
1004 msi_destroy_assembly_caches( package );
1005 return r;
1008 typedef struct {
1009 MSIPACKAGE *package;
1010 MSIFEATURE *feature;
1011 } _ilfs;
1013 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1015 ComponentList *cl;
1017 cl = msi_alloc( sizeof (*cl) );
1018 if ( !cl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 cl->component = comp;
1021 list_add_tail( &feature->Components, &cl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1028 FeatureList *fl;
1030 fl = msi_alloc( sizeof(*fl) );
1031 if ( !fl )
1032 return ERROR_NOT_ENOUGH_MEMORY;
1033 fl->feature = child;
1034 list_add_tail( &parent->Children, &fl->entry );
1036 return ERROR_SUCCESS;
1039 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1041 _ilfs* ilfs = param;
1042 LPCWSTR component;
1043 MSICOMPONENT *comp;
1045 component = MSI_RecordGetString(row,1);
1047 /* check to see if the component is already loaded */
1048 comp = msi_get_loaded_component( ilfs->package, component );
1049 if (!comp)
1051 WARN("ignoring unknown component %s\n", debugstr_w(component));
1052 return ERROR_SUCCESS;
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 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1113 msiobj_release(&view->hdr);
1114 return rc;
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 rc;
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 r = MSI_IterateRecords( view, NULL, load_media, package );
1348 msiobj_release( &view->hdr );
1349 return r;
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 rc;
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 == SCRIPT_ROLLBACK)
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;
1826 /* now we want to set component state based based on feature state */
1827 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1829 ComponentList *cl;
1831 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1832 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1833 feature->ActionRequest, feature->Action);
1835 if (!is_feature_selected( feature, level )) continue;
1837 /* features with components that have compressed files are made local */
1838 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1840 if (cl->component->ForceLocalState &&
1841 feature->ActionRequest == INSTALLSTATE_SOURCE)
1843 feature->Action = INSTALLSTATE_LOCAL;
1844 feature->ActionRequest = INSTALLSTATE_LOCAL;
1845 break;
1849 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1851 component = cl->component;
1853 switch (feature->ActionRequest)
1855 case INSTALLSTATE_ABSENT:
1856 component->anyAbsent = 1;
1857 break;
1858 case INSTALLSTATE_ADVERTISED:
1859 component->hasAdvertiseFeature = 1;
1860 break;
1861 case INSTALLSTATE_SOURCE:
1862 component->hasSourceFeature = 1;
1863 break;
1864 case INSTALLSTATE_LOCAL:
1865 component->hasLocalFeature = 1;
1866 break;
1867 case INSTALLSTATE_DEFAULT:
1868 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1869 component->hasAdvertiseFeature = 1;
1870 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1871 component->hasSourceFeature = 1;
1872 else
1873 component->hasLocalFeature = 1;
1874 break;
1875 default:
1876 break;
1881 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1883 /* check if it's local or source */
1884 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1885 (component->hasLocalFeature || component->hasSourceFeature))
1887 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1888 !component->ForceLocalState)
1890 component->Action = INSTALLSTATE_SOURCE;
1891 component->ActionRequest = INSTALLSTATE_SOURCE;
1893 else
1895 component->Action = INSTALLSTATE_LOCAL;
1896 component->ActionRequest = INSTALLSTATE_LOCAL;
1898 continue;
1901 /* if any feature is local, the component must be local too */
1902 if (component->hasLocalFeature)
1904 component->Action = INSTALLSTATE_LOCAL;
1905 component->ActionRequest = INSTALLSTATE_LOCAL;
1906 continue;
1908 if (component->hasSourceFeature)
1910 component->Action = INSTALLSTATE_SOURCE;
1911 component->ActionRequest = INSTALLSTATE_SOURCE;
1912 continue;
1914 if (component->hasAdvertiseFeature)
1916 component->Action = INSTALLSTATE_ADVERTISED;
1917 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1918 continue;
1920 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1921 if (component->anyAbsent &&
1922 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1924 component->Action = INSTALLSTATE_ABSENT;
1925 component->ActionRequest = INSTALLSTATE_ABSENT;
1929 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1931 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1933 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1934 component->Action = INSTALLSTATE_LOCAL;
1935 component->ActionRequest = INSTALLSTATE_LOCAL;
1938 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1939 component->Installed == INSTALLSTATE_SOURCE &&
1940 component->hasSourceFeature)
1942 component->Action = INSTALLSTATE_UNKNOWN;
1943 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1946 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1947 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1950 return ERROR_SUCCESS;
1953 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1955 MSIPACKAGE *package = param;
1956 LPCWSTR name;
1957 MSIFEATURE *feature;
1959 name = MSI_RecordGetString( row, 1 );
1961 feature = msi_get_loaded_feature( package, name );
1962 if (!feature)
1963 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1964 else
1966 LPCWSTR Condition;
1967 Condition = MSI_RecordGetString(row,3);
1969 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1971 int level = MSI_RecordGetInteger(row,2);
1972 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1973 feature->Level = level;
1976 return ERROR_SUCCESS;
1979 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1981 static const WCHAR name[] = {'\\',0};
1982 VS_FIXEDFILEINFO *ptr, *ret;
1983 LPVOID version;
1984 DWORD versize, handle;
1985 UINT sz;
1987 versize = GetFileVersionInfoSizeW( filename, &handle );
1988 if (!versize)
1989 return NULL;
1991 version = msi_alloc( versize );
1992 if (!version)
1993 return NULL;
1995 GetFileVersionInfoW( filename, 0, versize, version );
1997 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
1999 msi_free( version );
2000 return NULL;
2003 ret = msi_alloc( sz );
2004 memcpy( ret, ptr, sz );
2006 msi_free( version );
2007 return ret;
2010 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2012 DWORD ms, ls;
2014 msi_parse_version_string( version, &ms, &ls );
2016 if (fi->dwFileVersionMS > ms) return 1;
2017 else if (fi->dwFileVersionMS < ms) return -1;
2018 else if (fi->dwFileVersionLS > ls) return 1;
2019 else if (fi->dwFileVersionLS < ls) return -1;
2020 return 0;
2023 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2025 DWORD ms1, ms2;
2027 msi_parse_version_string( ver1, &ms1, NULL );
2028 msi_parse_version_string( ver2, &ms2, NULL );
2030 if (ms1 > ms2) return 1;
2031 else if (ms1 < ms2) return -1;
2032 return 0;
2035 DWORD msi_get_disk_file_size( LPCWSTR filename )
2037 HANDLE file;
2038 DWORD size;
2040 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2041 if (file == INVALID_HANDLE_VALUE)
2042 return INVALID_FILE_SIZE;
2044 size = GetFileSize( file, NULL );
2045 TRACE("size is %u\n", size);
2046 CloseHandle( file );
2047 return size;
2050 BOOL msi_file_hash_matches( MSIFILE *file )
2052 UINT r;
2053 MSIFILEHASHINFO hash;
2055 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2056 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2057 if (r != ERROR_SUCCESS)
2058 return FALSE;
2060 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2063 static WCHAR *get_temp_dir( void )
2065 static UINT id;
2066 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2068 GetTempPathW( MAX_PATH, tmp );
2069 for (;;)
2071 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2072 if (CreateDirectoryW( dir, NULL )) break;
2074 return strdupW( dir );
2078 * msi_build_directory_name()
2080 * This function is to save messing round with directory names
2081 * It handles adding backslashes between path segments,
2082 * and can add \ at the end of the directory name if told to.
2084 * It takes a variable number of arguments.
2085 * It always allocates a new string for the result, so make sure
2086 * to free the return value when finished with it.
2088 * The first arg is the number of path segments that follow.
2089 * The arguments following count are a list of path segments.
2090 * A path segment may be NULL.
2092 * Path segments will be added with a \ separating them.
2093 * A \ will not be added after the last segment, however if the
2094 * last segment is NULL, then the last character will be a \
2096 WCHAR *msi_build_directory_name( DWORD count, ... )
2098 DWORD sz = 1, i;
2099 WCHAR *dir;
2100 va_list va;
2102 va_start( va, count );
2103 for (i = 0; i < count; i++)
2105 const WCHAR *str = va_arg( va, const WCHAR * );
2106 if (str) sz += strlenW( str ) + 1;
2108 va_end( va );
2110 dir = msi_alloc( sz * sizeof(WCHAR) );
2111 dir[0] = 0;
2113 va_start( va, count );
2114 for (i = 0; i < count; i++)
2116 const WCHAR *str = va_arg( va, const WCHAR * );
2117 if (!str) continue;
2118 strcatW( dir, str );
2119 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2121 va_end( va );
2122 return dir;
2125 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2127 MSIASSEMBLY *assembly = file->Component->assembly;
2129 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2131 msi_free( file->TargetPath );
2132 if (assembly && !assembly->application)
2134 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2135 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2136 msi_track_tempfile( package, file->TargetPath );
2138 else
2140 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2141 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2144 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2147 static UINT calculate_file_cost( MSIPACKAGE *package )
2149 VS_FIXEDFILEINFO *file_version;
2150 WCHAR *font_version;
2151 MSIFILE *file;
2153 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2155 MSICOMPONENT *comp = file->Component;
2156 DWORD file_size;
2158 if (!comp->Enabled) continue;
2160 if (file->IsCompressed)
2161 comp->ForceLocalState = TRUE;
2163 set_target_path( package, file );
2165 if ((comp->assembly && !comp->assembly->installed) ||
2166 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2168 comp->Cost += file->FileSize;
2169 continue;
2171 file_size = msi_get_disk_file_size( file->TargetPath );
2173 if (file->Version)
2175 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2177 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2179 comp->Cost += file->FileSize - file_size;
2181 msi_free( file_version );
2182 continue;
2184 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2186 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2188 comp->Cost += file->FileSize - file_size;
2190 msi_free( font_version );
2191 continue;
2194 if (file_size != file->FileSize)
2196 comp->Cost += file->FileSize - file_size;
2199 return ERROR_SUCCESS;
2202 WCHAR *msi_normalize_path( const WCHAR *in )
2204 const WCHAR *p = in;
2205 WCHAR *q, *ret;
2206 int n, len = strlenW( in ) + 2;
2208 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2210 len = 0;
2211 while (1)
2213 /* copy until the end of the string or a space */
2214 while (*p != ' ' && (*q = *p))
2216 p++, len++;
2217 /* reduce many backslashes to one */
2218 if (*p != '\\' || *q != '\\')
2219 q++;
2222 /* quit at the end of the string */
2223 if (!*p)
2224 break;
2226 /* count the number of spaces */
2227 n = 0;
2228 while (p[n] == ' ')
2229 n++;
2231 /* if it's leading or trailing space, skip it */
2232 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2233 p += n;
2234 else /* copy n spaces */
2235 while (n && (*q++ = *p++)) n--;
2237 while (q - ret > 0 && q[-1] == ' ') q--;
2238 if (q - ret > 0 && q[-1] != '\\')
2240 q[0] = '\\';
2241 q[1] = 0;
2243 return ret;
2246 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2248 FolderList *fl;
2249 MSIFOLDER *folder, *parent, *child;
2250 WCHAR *path, *normalized_path;
2252 TRACE("resolving %s\n", debugstr_w(name));
2254 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2256 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2258 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
2260 path = msi_dup_property( package->db, szRootDrive );
2263 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2265 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2267 parent = msi_get_loaded_folder( package, folder->Parent );
2268 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2270 else
2271 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2273 normalized_path = msi_normalize_path( path );
2274 msi_free( path );
2275 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2277 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2278 msi_free( normalized_path );
2279 return;
2281 msi_set_property( package->db, folder->Directory, normalized_path );
2282 msi_free( folder->ResolvedTarget );
2283 folder->ResolvedTarget = normalized_path;
2285 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2287 child = fl->folder;
2288 msi_resolve_target_folder( package, child->Directory, load_prop );
2290 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2293 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2295 static const WCHAR query[] = {
2296 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2297 '`','C','o','n','d','i','t','i','o','n','`',0};
2298 static const WCHAR szOutOfDiskSpace[] = {
2299 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2300 MSICOMPONENT *comp;
2301 MSIQUERY *view;
2302 LPWSTR level;
2303 UINT rc;
2305 TRACE("Building directory properties\n");
2306 msi_resolve_target_folder( package, szTargetDir, TRUE );
2308 TRACE("Evaluating component conditions\n");
2309 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2311 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2313 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2314 comp->Enabled = FALSE;
2316 else
2317 comp->Enabled = TRUE;
2320 /* read components states from the registry */
2321 ACTION_GetComponentInstallStates(package);
2322 ACTION_GetFeatureInstallStates(package);
2324 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2326 TRACE("Evaluating feature conditions\n");
2328 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2329 if (rc == ERROR_SUCCESS)
2331 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2332 msiobj_release( &view->hdr );
2333 if (rc != ERROR_SUCCESS)
2334 return rc;
2338 TRACE("Calculating file cost\n");
2339 calculate_file_cost( package );
2341 msi_set_property( package->db, szCostingComplete, szOne );
2342 /* set default run level if not set */
2343 level = msi_dup_property( package->db, szInstallLevel );
2344 if (!level)
2345 msi_set_property( package->db, szInstallLevel, szOne );
2346 msi_free(level);
2348 /* FIXME: check volume disk space */
2349 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2351 return MSI_SetFeatureStates(package);
2354 /* OK this value is "interpreted" and then formatted based on the
2355 first few characters */
2356 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2357 DWORD *size)
2359 LPSTR data = NULL;
2361 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2363 if (value[1]=='x')
2365 LPWSTR ptr;
2366 CHAR byte[5];
2367 LPWSTR deformated = NULL;
2368 int count;
2370 deformat_string(package, &value[2], &deformated);
2372 /* binary value type */
2373 ptr = deformated;
2374 *type = REG_BINARY;
2375 if (strlenW(ptr)%2)
2376 *size = (strlenW(ptr)/2)+1;
2377 else
2378 *size = strlenW(ptr)/2;
2380 data = msi_alloc(*size);
2382 byte[0] = '0';
2383 byte[1] = 'x';
2384 byte[4] = 0;
2385 count = 0;
2386 /* if uneven pad with a zero in front */
2387 if (strlenW(ptr)%2)
2389 byte[2]= '0';
2390 byte[3]= *ptr;
2391 ptr++;
2392 data[count] = (BYTE)strtol(byte,NULL,0);
2393 count ++;
2394 TRACE("Uneven byte count\n");
2396 while (*ptr)
2398 byte[2]= *ptr;
2399 ptr++;
2400 byte[3]= *ptr;
2401 ptr++;
2402 data[count] = (BYTE)strtol(byte,NULL,0);
2403 count ++;
2405 msi_free(deformated);
2407 TRACE("Data %i bytes(%i)\n",*size,count);
2409 else
2411 LPWSTR deformated;
2412 LPWSTR p;
2413 DWORD d = 0;
2414 deformat_string(package, &value[1], &deformated);
2416 *type=REG_DWORD;
2417 *size = sizeof(DWORD);
2418 data = msi_alloc(*size);
2419 p = deformated;
2420 if (*p == '-')
2421 p++;
2422 while (*p)
2424 if ( (*p < '0') || (*p > '9') )
2425 break;
2426 d *= 10;
2427 d += (*p - '0');
2428 p++;
2430 if (deformated[0] == '-')
2431 d = -d;
2432 *(LPDWORD)data = d;
2433 TRACE("DWORD %i\n",*(LPDWORD)data);
2435 msi_free(deformated);
2438 else
2440 static const WCHAR szMulti[] = {'[','~',']',0};
2441 LPCWSTR ptr;
2442 *type=REG_SZ;
2444 if (value[0]=='#')
2446 if (value[1]=='%')
2448 ptr = &value[2];
2449 *type=REG_EXPAND_SZ;
2451 else
2452 ptr = &value[1];
2454 else
2455 ptr=value;
2457 if (strstrW(value, szMulti))
2458 *type = REG_MULTI_SZ;
2460 /* remove initial delimiter */
2461 if (!strncmpW(value, szMulti, 3))
2462 ptr = value + 3;
2464 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2466 /* add double NULL terminator */
2467 if (*type == REG_MULTI_SZ)
2469 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2470 data = msi_realloc_zero(data, *size);
2473 return data;
2476 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2478 const WCHAR *ret;
2480 switch (root)
2482 case -1:
2483 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2485 *root_key = HKEY_LOCAL_MACHINE;
2486 ret = szHLM;
2488 else
2490 *root_key = HKEY_CURRENT_USER;
2491 ret = szHCU;
2493 break;
2494 case 0:
2495 *root_key = HKEY_CLASSES_ROOT;
2496 ret = szHCR;
2497 break;
2498 case 1:
2499 *root_key = HKEY_CURRENT_USER;
2500 ret = szHCU;
2501 break;
2502 case 2:
2503 *root_key = HKEY_LOCAL_MACHINE;
2504 ret = szHLM;
2505 break;
2506 case 3:
2507 *root_key = HKEY_USERS;
2508 ret = szHU;
2509 break;
2510 default:
2511 ERR("Unknown root %i\n", root);
2512 return NULL;
2515 return ret;
2518 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2520 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2521 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2523 if (is_64bit && package->platform == PLATFORM_INTEL &&
2524 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2526 UINT size;
2527 WCHAR *path_32node;
2529 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2530 if (!(path_32node = msi_alloc( size ))) return NULL;
2532 memcpy( path_32node, path, len * sizeof(WCHAR) );
2533 strcpyW( path_32node + len, szWow6432Node );
2534 strcatW( path_32node, szBackSlash );
2535 strcatW( path_32node, path + len );
2536 return path_32node;
2539 return strdupW( path );
2542 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2544 MSIPACKAGE *package = param;
2545 LPSTR value_data = NULL;
2546 HKEY root_key, hkey;
2547 DWORD type,size;
2548 LPWSTR deformated, uikey, keypath;
2549 LPCWSTR szRoot, component, name, key, value;
2550 MSICOMPONENT *comp;
2551 MSIRECORD * uirow;
2552 INT root;
2553 BOOL check_first = FALSE;
2554 UINT rc;
2556 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2558 component = MSI_RecordGetString(row, 6);
2559 comp = msi_get_loaded_component(package,component);
2560 if (!comp)
2561 return ERROR_SUCCESS;
2563 comp->Action = msi_get_component_action( package, comp );
2564 if (comp->Action != INSTALLSTATE_LOCAL)
2566 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2567 return ERROR_SUCCESS;
2570 name = MSI_RecordGetString(row, 4);
2571 if( MSI_RecordIsNull(row,5) && name )
2573 /* null values can have special meanings */
2574 if (name[0]=='-' && name[1] == 0)
2575 return ERROR_SUCCESS;
2576 else if ((name[0]=='+' && name[1] == 0) ||
2577 (name[0] == '*' && name[1] == 0))
2578 name = NULL;
2579 check_first = TRUE;
2582 root = MSI_RecordGetInteger(row,2);
2583 key = MSI_RecordGetString(row, 3);
2585 szRoot = get_root_key( package, root, &root_key );
2586 if (!szRoot)
2587 return ERROR_SUCCESS;
2589 deformat_string(package, key , &deformated);
2590 size = strlenW(deformated) + strlenW(szRoot) + 1;
2591 uikey = msi_alloc(size*sizeof(WCHAR));
2592 strcpyW(uikey,szRoot);
2593 strcatW(uikey,deformated);
2595 keypath = get_keypath( package, root_key, deformated );
2596 msi_free( deformated );
2597 if (RegCreateKeyW( root_key, keypath, &hkey ))
2599 ERR("Could not create key %s\n", debugstr_w(keypath));
2600 msi_free(uikey);
2601 msi_free(keypath);
2602 return ERROR_SUCCESS;
2605 value = MSI_RecordGetString(row,5);
2606 if (value)
2607 value_data = parse_value(package, value, &type, &size);
2608 else
2610 value_data = (LPSTR)strdupW(szEmpty);
2611 size = sizeof(szEmpty);
2612 type = REG_SZ;
2615 deformat_string(package, name, &deformated);
2617 if (!check_first)
2619 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2620 debugstr_w(uikey));
2621 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2623 else
2625 DWORD sz = 0;
2626 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2627 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2629 TRACE("value %s of %s checked already exists\n",
2630 debugstr_w(deformated), debugstr_w(uikey));
2632 else
2634 TRACE("Checked and setting value %s of %s\n",
2635 debugstr_w(deformated), debugstr_w(uikey));
2636 if (deformated || size)
2637 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2640 RegCloseKey(hkey);
2642 uirow = MSI_CreateRecord(3);
2643 MSI_RecordSetStringW(uirow,2,deformated);
2644 MSI_RecordSetStringW(uirow,1,uikey);
2645 if (type == REG_SZ || type == REG_EXPAND_SZ)
2646 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2647 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2648 msiobj_release( &uirow->hdr );
2650 msi_free(value_data);
2651 msi_free(deformated);
2652 msi_free(uikey);
2653 msi_free(keypath);
2655 return ERROR_SUCCESS;
2658 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2660 static const WCHAR query[] = {
2661 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2662 '`','R','e','g','i','s','t','r','y','`',0};
2663 MSIQUERY *view;
2664 UINT rc;
2666 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2667 if (rc != ERROR_SUCCESS)
2668 return ERROR_SUCCESS;
2670 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2671 msiobj_release(&view->hdr);
2672 return rc;
2675 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2677 LONG res;
2678 HKEY hkey;
2679 DWORD num_subkeys, num_values;
2681 if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2683 if ((res = RegDeleteValueW( hkey, value )))
2685 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2687 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2688 NULL, NULL, NULL, NULL );
2689 RegCloseKey( hkey );
2690 if (!res && !num_subkeys && !num_values)
2692 TRACE("removing empty key %s\n", debugstr_w(keypath));
2693 RegDeleteKeyW( root, keypath );
2695 return;
2697 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2700 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2702 LONG res = RegDeleteTreeW( root, keypath );
2703 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2706 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2708 MSIPACKAGE *package = param;
2709 LPCWSTR component, name, key_str, root_key_str;
2710 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2711 MSICOMPONENT *comp;
2712 MSIRECORD *uirow;
2713 BOOL delete_key = FALSE;
2714 HKEY hkey_root;
2715 UINT size;
2716 INT root;
2718 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2720 component = MSI_RecordGetString( row, 6 );
2721 comp = msi_get_loaded_component( package, component );
2722 if (!comp)
2723 return ERROR_SUCCESS;
2725 comp->Action = msi_get_component_action( package, comp );
2726 if (comp->Action != INSTALLSTATE_ABSENT)
2728 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2729 return ERROR_SUCCESS;
2732 name = MSI_RecordGetString( row, 4 );
2733 if (MSI_RecordIsNull( row, 5 ) && name )
2735 if (name[0] == '+' && !name[1])
2736 return ERROR_SUCCESS;
2737 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2739 delete_key = TRUE;
2740 name = NULL;
2744 root = MSI_RecordGetInteger( row, 2 );
2745 key_str = MSI_RecordGetString( row, 3 );
2747 root_key_str = get_root_key( package, root, &hkey_root );
2748 if (!root_key_str)
2749 return ERROR_SUCCESS;
2751 deformat_string( package, key_str, &deformated_key );
2752 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2753 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2754 strcpyW( ui_key_str, root_key_str );
2755 strcatW( ui_key_str, deformated_key );
2757 deformat_string( package, name, &deformated_name );
2759 keypath = get_keypath( package, hkey_root, deformated_key );
2760 msi_free( deformated_key );
2761 if (delete_key) delete_reg_key( hkey_root, keypath );
2762 else delete_reg_value( hkey_root, keypath, deformated_name );
2763 msi_free( keypath );
2765 uirow = MSI_CreateRecord( 2 );
2766 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2767 MSI_RecordSetStringW( uirow, 2, deformated_name );
2768 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2769 msiobj_release( &uirow->hdr );
2771 msi_free( ui_key_str );
2772 msi_free( deformated_name );
2773 return ERROR_SUCCESS;
2776 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2778 MSIPACKAGE *package = param;
2779 LPCWSTR component, name, key_str, root_key_str;
2780 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2781 MSICOMPONENT *comp;
2782 MSIRECORD *uirow;
2783 BOOL delete_key = FALSE;
2784 HKEY hkey_root;
2785 UINT size;
2786 INT root;
2788 component = MSI_RecordGetString( row, 5 );
2789 comp = msi_get_loaded_component( package, component );
2790 if (!comp)
2791 return ERROR_SUCCESS;
2793 comp->Action = msi_get_component_action( package, comp );
2794 if (comp->Action != INSTALLSTATE_LOCAL)
2796 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2797 return ERROR_SUCCESS;
2800 if ((name = MSI_RecordGetString( row, 4 )))
2802 if (name[0] == '-' && !name[1])
2804 delete_key = TRUE;
2805 name = NULL;
2809 root = MSI_RecordGetInteger( row, 2 );
2810 key_str = MSI_RecordGetString( row, 3 );
2812 root_key_str = get_root_key( package, root, &hkey_root );
2813 if (!root_key_str)
2814 return ERROR_SUCCESS;
2816 deformat_string( package, key_str, &deformated_key );
2817 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2818 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2819 strcpyW( ui_key_str, root_key_str );
2820 strcatW( ui_key_str, deformated_key );
2822 deformat_string( package, name, &deformated_name );
2824 keypath = get_keypath( package, hkey_root, deformated_key );
2825 msi_free( deformated_key );
2826 if (delete_key) delete_reg_key( hkey_root, keypath );
2827 else delete_reg_value( hkey_root, keypath, deformated_name );
2828 msi_free( keypath );
2830 uirow = MSI_CreateRecord( 2 );
2831 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2832 MSI_RecordSetStringW( uirow, 2, deformated_name );
2833 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2834 msiobj_release( &uirow->hdr );
2836 msi_free( ui_key_str );
2837 msi_free( deformated_name );
2838 return ERROR_SUCCESS;
2841 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2843 static const WCHAR registry_query[] = {
2844 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2845 '`','R','e','g','i','s','t','r','y','`',0};
2846 static const WCHAR remove_registry_query[] = {
2847 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2848 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2849 MSIQUERY *view;
2850 UINT rc;
2852 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2853 if (rc == ERROR_SUCCESS)
2855 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2856 msiobj_release( &view->hdr );
2857 if (rc != ERROR_SUCCESS)
2858 return rc;
2860 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2861 if (rc == ERROR_SUCCESS)
2863 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2864 msiobj_release( &view->hdr );
2865 if (rc != ERROR_SUCCESS)
2866 return rc;
2868 return ERROR_SUCCESS;
2871 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2873 package->script->CurrentlyScripting = TRUE;
2875 return ERROR_SUCCESS;
2879 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2881 static const WCHAR query[]= {
2882 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2883 '`','R','e','g','i','s','t','r','y','`',0};
2884 MSICOMPONENT *comp;
2885 DWORD total = 0, count = 0;
2886 MSIQUERY *view;
2887 MSIFEATURE *feature;
2888 MSIFILE *file;
2889 UINT rc;
2891 TRACE("InstallValidate\n");
2893 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2894 if (rc == ERROR_SUCCESS)
2896 rc = MSI_IterateRecords( view, &count, NULL, package );
2897 msiobj_release( &view->hdr );
2898 if (rc != ERROR_SUCCESS)
2899 return rc;
2900 total += count * REG_PROGRESS_VALUE;
2902 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2903 total += COMPONENT_PROGRESS_VALUE;
2905 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2906 total += file->FileSize;
2908 msi_ui_progress( package, 0, total, 0, 0 );
2910 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2912 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2913 debugstr_w(feature->Feature), feature->Installed,
2914 feature->ActionRequest, feature->Action);
2916 return ERROR_SUCCESS;
2919 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2921 MSIPACKAGE* package = param;
2922 LPCWSTR cond = NULL;
2923 LPCWSTR message = NULL;
2924 UINT r;
2926 static const WCHAR title[]=
2927 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2929 cond = MSI_RecordGetString(row,1);
2931 r = MSI_EvaluateConditionW(package,cond);
2932 if (r == MSICONDITION_FALSE)
2934 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2936 LPWSTR deformated;
2937 message = MSI_RecordGetString(row,2);
2938 deformat_string(package,message,&deformated);
2939 MessageBoxW(NULL,deformated,title,MB_OK);
2940 msi_free(deformated);
2943 return ERROR_INSTALL_FAILURE;
2946 return ERROR_SUCCESS;
2949 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2951 static const WCHAR query[] = {
2952 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2953 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2954 MSIQUERY *view;
2955 UINT rc;
2957 TRACE("Checking launch conditions\n");
2959 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2960 if (rc != ERROR_SUCCESS)
2961 return ERROR_SUCCESS;
2963 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2964 msiobj_release(&view->hdr);
2965 return rc;
2968 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2971 if (!cmp->KeyPath)
2972 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2974 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2976 static const WCHAR query[] = {
2977 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2978 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2979 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2980 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
2981 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2982 MSIRECORD *row;
2983 UINT root, len;
2984 LPWSTR deformated, buffer, deformated_name;
2985 LPCWSTR key, name;
2987 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
2988 if (!row)
2989 return NULL;
2991 root = MSI_RecordGetInteger(row,2);
2992 key = MSI_RecordGetString(row, 3);
2993 name = MSI_RecordGetString(row, 4);
2994 deformat_string(package, key , &deformated);
2995 deformat_string(package, name, &deformated_name);
2997 len = strlenW(deformated) + 6;
2998 if (deformated_name)
2999 len+=strlenW(deformated_name);
3001 buffer = msi_alloc( len *sizeof(WCHAR));
3003 if (deformated_name)
3004 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3005 else
3006 sprintfW(buffer,fmt,root,deformated);
3008 msi_free(deformated);
3009 msi_free(deformated_name);
3010 msiobj_release(&row->hdr);
3012 return buffer;
3014 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3016 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3017 return NULL;
3019 else
3021 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3023 if (file)
3024 return strdupW( file->TargetPath );
3026 return NULL;
3029 static HKEY openSharedDLLsKey(void)
3031 HKEY hkey=0;
3032 static const WCHAR path[] =
3033 {'S','o','f','t','w','a','r','e','\\',
3034 'M','i','c','r','o','s','o','f','t','\\',
3035 'W','i','n','d','o','w','s','\\',
3036 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3037 'S','h','a','r','e','d','D','L','L','s',0};
3039 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3040 return hkey;
3043 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3045 HKEY hkey;
3046 DWORD count=0;
3047 DWORD type;
3048 DWORD sz = sizeof(count);
3049 DWORD rc;
3051 hkey = openSharedDLLsKey();
3052 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3053 if (rc != ERROR_SUCCESS)
3054 count = 0;
3055 RegCloseKey(hkey);
3056 return count;
3059 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3061 HKEY hkey;
3063 hkey = openSharedDLLsKey();
3064 if (count > 0)
3065 msi_reg_set_val_dword( hkey, path, count );
3066 else
3067 RegDeleteValueW(hkey,path);
3068 RegCloseKey(hkey);
3069 return count;
3072 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3074 MSIFEATURE *feature;
3075 INT count = 0;
3076 BOOL write = FALSE;
3078 /* only refcount DLLs */
3079 if (comp->KeyPath == NULL ||
3080 comp->assembly ||
3081 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3082 comp->Attributes & msidbComponentAttributesODBCDataSource)
3083 write = FALSE;
3084 else
3086 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3087 write = (count > 0);
3089 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3090 write = TRUE;
3093 /* increment counts */
3094 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3096 ComponentList *cl;
3098 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3099 continue;
3101 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3103 if ( cl->component == comp )
3104 count++;
3108 /* decrement counts */
3109 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3111 ComponentList *cl;
3113 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3114 continue;
3116 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3118 if ( cl->component == comp )
3119 count--;
3123 /* ref count all the files in the component */
3124 if (write)
3126 MSIFILE *file;
3128 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3130 if (file->Component == comp)
3131 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3135 /* add a count for permanent */
3136 if (comp->Attributes & msidbComponentAttributesPermanent)
3137 count ++;
3139 comp->RefCount = count;
3141 if (write)
3142 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3145 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3147 if (comp->assembly)
3149 const WCHAR prefixW[] = {'<','\\',0};
3150 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3151 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3153 if (keypath)
3155 strcpyW( keypath, prefixW );
3156 strcatW( keypath, comp->assembly->display_name );
3158 return keypath;
3160 return resolve_keypath( package, comp );
3163 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3165 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3166 UINT rc;
3167 MSICOMPONENT *comp;
3168 HKEY hkey;
3170 TRACE("\n");
3172 squash_guid(package->ProductCode,squished_pc);
3173 msi_set_sourcedir_props(package, FALSE);
3175 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3177 MSIRECORD *uirow;
3178 INSTALLSTATE action;
3180 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3181 if (!comp->ComponentId)
3182 continue;
3184 squash_guid( comp->ComponentId, squished_cc );
3185 msi_free( comp->FullKeypath );
3186 comp->FullKeypath = build_full_keypath( package, comp );
3188 ACTION_RefCountComponent( package, comp );
3190 if (package->need_rollback) action = comp->Installed;
3191 else action = comp->ActionRequest;
3193 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3194 debugstr_w(comp->Component), debugstr_w(squished_cc),
3195 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3197 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3199 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3200 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3201 else
3202 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3204 if (rc != ERROR_SUCCESS)
3205 continue;
3207 if (comp->Attributes & msidbComponentAttributesPermanent)
3209 static const WCHAR szPermKey[] =
3210 { '0','0','0','0','0','0','0','0','0','0','0','0',
3211 '0','0','0','0','0','0','0','0','0','0','0','0',
3212 '0','0','0','0','0','0','0','0',0 };
3214 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3216 if (action == INSTALLSTATE_LOCAL)
3217 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3218 else
3220 MSIFILE *file;
3221 MSIRECORD *row;
3222 LPWSTR ptr, ptr2;
3223 WCHAR source[MAX_PATH];
3224 WCHAR base[MAX_PATH];
3225 LPWSTR sourcepath;
3227 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3228 static const WCHAR query[] = {
3229 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3230 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3231 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3232 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3233 '`','D','i','s','k','I','d','`',0};
3235 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3236 continue;
3238 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3239 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3240 ptr2 = strrchrW(source, '\\') + 1;
3241 msiobj_release(&row->hdr);
3243 lstrcpyW(base, package->PackagePath);
3244 ptr = strrchrW(base, '\\');
3245 *(ptr + 1) = '\0';
3247 sourcepath = msi_resolve_file_source(package, file);
3248 ptr = sourcepath + lstrlenW(base);
3249 lstrcpyW(ptr2, ptr);
3250 msi_free(sourcepath);
3252 msi_reg_set_val_str(hkey, squished_pc, source);
3254 RegCloseKey(hkey);
3256 else if (action == INSTALLSTATE_ABSENT)
3258 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3259 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3260 else
3261 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3264 /* UI stuff */
3265 uirow = MSI_CreateRecord(3);
3266 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3267 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3268 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3269 msi_ui_actiondata( package, szProcessComponents, uirow );
3270 msiobj_release( &uirow->hdr );
3272 return ERROR_SUCCESS;
3275 typedef struct {
3276 CLSID clsid;
3277 LPWSTR source;
3279 LPWSTR path;
3280 ITypeLib *ptLib;
3281 } typelib_struct;
3283 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3284 LPWSTR lpszName, LONG_PTR lParam)
3286 TLIBATTR *attr;
3287 typelib_struct *tl_struct = (typelib_struct*) lParam;
3288 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3289 int sz;
3290 HRESULT res;
3292 if (!IS_INTRESOURCE(lpszName))
3294 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3295 return TRUE;
3298 sz = strlenW(tl_struct->source)+4;
3299 sz *= sizeof(WCHAR);
3301 if ((INT_PTR)lpszName == 1)
3302 tl_struct->path = strdupW(tl_struct->source);
3303 else
3305 tl_struct->path = msi_alloc(sz);
3306 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3309 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3310 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3311 if (FAILED(res))
3313 msi_free(tl_struct->path);
3314 tl_struct->path = NULL;
3316 return TRUE;
3319 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3320 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3322 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3323 return FALSE;
3326 msi_free(tl_struct->path);
3327 tl_struct->path = NULL;
3329 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3330 ITypeLib_Release(tl_struct->ptLib);
3332 return TRUE;
3335 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3337 MSIPACKAGE* package = param;
3338 LPCWSTR component;
3339 MSICOMPONENT *comp;
3340 MSIFILE *file;
3341 typelib_struct tl_struct;
3342 ITypeLib *tlib;
3343 HMODULE module;
3344 HRESULT hr;
3346 component = MSI_RecordGetString(row,3);
3347 comp = msi_get_loaded_component(package,component);
3348 if (!comp)
3349 return ERROR_SUCCESS;
3351 comp->Action = msi_get_component_action( package, comp );
3352 if (comp->Action != INSTALLSTATE_LOCAL)
3354 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3355 return ERROR_SUCCESS;
3358 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3360 TRACE("component has no key path\n");
3361 return ERROR_SUCCESS;
3363 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3365 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3366 if (module)
3368 LPCWSTR guid;
3369 guid = MSI_RecordGetString(row,1);
3370 CLSIDFromString( guid, &tl_struct.clsid);
3371 tl_struct.source = strdupW( file->TargetPath );
3372 tl_struct.path = NULL;
3374 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3375 (LONG_PTR)&tl_struct);
3377 if (tl_struct.path)
3379 LPCWSTR helpid, help_path = NULL;
3380 HRESULT res;
3382 helpid = MSI_RecordGetString(row,6);
3384 if (helpid) help_path = msi_get_target_folder( package, helpid );
3385 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3387 if (FAILED(res))
3388 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3389 else
3390 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3392 ITypeLib_Release(tl_struct.ptLib);
3393 msi_free(tl_struct.path);
3395 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3397 FreeLibrary(module);
3398 msi_free(tl_struct.source);
3400 else
3402 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3403 if (FAILED(hr))
3405 ERR("Failed to load type library: %08x\n", hr);
3406 return ERROR_INSTALL_FAILURE;
3409 ITypeLib_Release(tlib);
3412 return ERROR_SUCCESS;
3415 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3417 static const WCHAR query[] = {
3418 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3419 '`','T','y','p','e','L','i','b','`',0};
3420 MSIQUERY *view;
3421 UINT rc;
3423 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3424 if (rc != ERROR_SUCCESS)
3425 return ERROR_SUCCESS;
3427 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3428 msiobj_release(&view->hdr);
3429 return rc;
3432 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3434 MSIPACKAGE *package = param;
3435 LPCWSTR component, guid;
3436 MSICOMPONENT *comp;
3437 GUID libid;
3438 UINT version;
3439 LCID language;
3440 SYSKIND syskind;
3441 HRESULT hr;
3443 component = MSI_RecordGetString( row, 3 );
3444 comp = msi_get_loaded_component( package, component );
3445 if (!comp)
3446 return ERROR_SUCCESS;
3448 comp->Action = msi_get_component_action( package, comp );
3449 if (comp->Action != INSTALLSTATE_ABSENT)
3451 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3452 return ERROR_SUCCESS;
3454 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3456 guid = MSI_RecordGetString( row, 1 );
3457 CLSIDFromString( guid, &libid );
3458 version = MSI_RecordGetInteger( row, 4 );
3459 language = MSI_RecordGetInteger( row, 2 );
3461 #ifdef _WIN64
3462 syskind = SYS_WIN64;
3463 #else
3464 syskind = SYS_WIN32;
3465 #endif
3467 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3468 if (FAILED(hr))
3470 WARN("Failed to unregister typelib: %08x\n", hr);
3473 return ERROR_SUCCESS;
3476 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3478 static const WCHAR query[] = {
3479 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3480 '`','T','y','p','e','L','i','b','`',0};
3481 MSIQUERY *view;
3482 UINT rc;
3484 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3485 if (rc != ERROR_SUCCESS)
3486 return ERROR_SUCCESS;
3488 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3489 msiobj_release( &view->hdr );
3490 return rc;
3493 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3495 static const WCHAR szlnk[] = {'.','l','n','k',0};
3496 LPCWSTR directory, extension, link_folder;
3497 LPWSTR link_file, filename;
3499 directory = MSI_RecordGetString( row, 2 );
3500 link_folder = msi_get_target_folder( package, directory );
3501 if (!link_folder)
3503 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3504 return NULL;
3506 /* may be needed because of a bug somewhere else */
3507 msi_create_full_path( link_folder );
3509 filename = msi_dup_record_field( row, 3 );
3510 msi_reduce_to_long_filename( filename );
3512 extension = strchrW( filename, '.' );
3513 if (!extension || strcmpiW( extension, szlnk ))
3515 int len = strlenW( filename );
3516 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3517 memcpy( filename + len, szlnk, sizeof(szlnk) );
3519 link_file = msi_build_directory_name( 2, link_folder, filename );
3520 msi_free( filename );
3522 return link_file;
3525 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3527 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3528 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3529 WCHAR *folder, *dest, *path;
3531 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3532 folder = msi_dup_property( package->db, szWindowsFolder );
3533 else
3535 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3536 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3537 msi_free( appdata );
3539 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3540 msi_create_full_path( dest );
3541 path = msi_build_directory_name( 2, dest, icon_name );
3542 msi_free( folder );
3543 msi_free( dest );
3544 return path;
3547 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3549 MSIPACKAGE *package = param;
3550 LPWSTR link_file, deformated, path;
3551 LPCWSTR component, target;
3552 MSICOMPONENT *comp;
3553 IShellLinkW *sl = NULL;
3554 IPersistFile *pf = NULL;
3555 HRESULT res;
3557 component = MSI_RecordGetString(row, 4);
3558 comp = msi_get_loaded_component(package, component);
3559 if (!comp)
3560 return ERROR_SUCCESS;
3562 comp->Action = msi_get_component_action( package, comp );
3563 if (comp->Action != INSTALLSTATE_LOCAL)
3565 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3566 return ERROR_SUCCESS;
3568 msi_ui_actiondata( package, szCreateShortcuts, row );
3570 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3571 &IID_IShellLinkW, (LPVOID *) &sl );
3573 if (FAILED( res ))
3575 ERR("CLSID_ShellLink not available\n");
3576 goto err;
3579 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3580 if (FAILED( res ))
3582 ERR("QueryInterface(IID_IPersistFile) failed\n");
3583 goto err;
3586 target = MSI_RecordGetString(row, 5);
3587 if (strchrW(target, '['))
3589 deformat_string( package, target, &path );
3590 TRACE("target path is %s\n", debugstr_w(path));
3591 IShellLinkW_SetPath( sl, path );
3592 msi_free( path );
3594 else
3596 FIXME("poorly handled shortcut format, advertised shortcut\n");
3597 IShellLinkW_SetPath(sl,comp->FullKeypath);
3600 if (!MSI_RecordIsNull(row,6))
3602 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3603 deformat_string(package, arguments, &deformated);
3604 IShellLinkW_SetArguments(sl,deformated);
3605 msi_free(deformated);
3608 if (!MSI_RecordIsNull(row,7))
3610 LPCWSTR description = MSI_RecordGetString(row, 7);
3611 IShellLinkW_SetDescription(sl, description);
3614 if (!MSI_RecordIsNull(row,8))
3615 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3617 if (!MSI_RecordIsNull(row,9))
3619 INT index;
3620 LPCWSTR icon = MSI_RecordGetString(row, 9);
3622 path = msi_build_icon_path(package, icon);
3623 index = MSI_RecordGetInteger(row,10);
3625 /* no value means 0 */
3626 if (index == MSI_NULL_INTEGER)
3627 index = 0;
3629 IShellLinkW_SetIconLocation(sl, path, index);
3630 msi_free(path);
3633 if (!MSI_RecordIsNull(row,11))
3634 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3636 if (!MSI_RecordIsNull(row,12))
3638 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3639 full_path = msi_get_target_folder( package, wkdir );
3640 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3642 link_file = get_link_file(package, row);
3644 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3645 IPersistFile_Save(pf, link_file, FALSE);
3646 msi_free(link_file);
3648 err:
3649 if (pf)
3650 IPersistFile_Release( pf );
3651 if (sl)
3652 IShellLinkW_Release( sl );
3654 return ERROR_SUCCESS;
3657 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3659 static const WCHAR query[] = {
3660 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3661 '`','S','h','o','r','t','c','u','t','`',0};
3662 MSIQUERY *view;
3663 HRESULT res;
3664 UINT rc;
3666 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3667 if (rc != ERROR_SUCCESS)
3668 return ERROR_SUCCESS;
3670 res = CoInitialize( NULL );
3672 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3673 msiobj_release(&view->hdr);
3675 if (SUCCEEDED(res)) CoUninitialize();
3676 return rc;
3679 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3681 MSIPACKAGE *package = param;
3682 LPWSTR link_file;
3683 LPCWSTR component;
3684 MSICOMPONENT *comp;
3686 component = MSI_RecordGetString( row, 4 );
3687 comp = msi_get_loaded_component( package, component );
3688 if (!comp)
3689 return ERROR_SUCCESS;
3691 comp->Action = msi_get_component_action( package, comp );
3692 if (comp->Action != INSTALLSTATE_ABSENT)
3694 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3695 return ERROR_SUCCESS;
3697 msi_ui_actiondata( package, szRemoveShortcuts, row );
3699 link_file = get_link_file( package, row );
3701 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3702 if (!DeleteFileW( link_file ))
3704 WARN("Failed to remove shortcut file %u\n", GetLastError());
3706 msi_free( link_file );
3708 return ERROR_SUCCESS;
3711 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3713 static const WCHAR query[] = {
3714 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3715 '`','S','h','o','r','t','c','u','t','`',0};
3716 MSIQUERY *view;
3717 UINT rc;
3719 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3720 if (rc != ERROR_SUCCESS)
3721 return ERROR_SUCCESS;
3723 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3724 msiobj_release( &view->hdr );
3725 return rc;
3728 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3730 MSIPACKAGE* package = param;
3731 HANDLE the_file;
3732 LPWSTR FilePath;
3733 LPCWSTR FileName;
3734 CHAR buffer[1024];
3735 DWORD sz;
3736 UINT rc;
3738 FileName = MSI_RecordGetString(row,1);
3739 if (!FileName)
3741 ERR("Unable to get FileName\n");
3742 return ERROR_SUCCESS;
3745 FilePath = msi_build_icon_path(package, FileName);
3747 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3749 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3750 FILE_ATTRIBUTE_NORMAL, NULL);
3752 if (the_file == INVALID_HANDLE_VALUE)
3754 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3755 msi_free(FilePath);
3756 return ERROR_SUCCESS;
3761 DWORD write;
3762 sz = 1024;
3763 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3764 if (rc != ERROR_SUCCESS)
3766 ERR("Failed to get stream\n");
3767 CloseHandle(the_file);
3768 DeleteFileW(FilePath);
3769 break;
3771 WriteFile(the_file,buffer,sz,&write,NULL);
3772 } while (sz == 1024);
3774 msi_free(FilePath);
3775 CloseHandle(the_file);
3777 return ERROR_SUCCESS;
3780 static UINT msi_publish_icons(MSIPACKAGE *package)
3782 static const WCHAR query[]= {
3783 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3784 '`','I','c','o','n','`',0};
3785 MSIQUERY *view;
3786 UINT r;
3788 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3789 if (r == ERROR_SUCCESS)
3791 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3792 msiobj_release(&view->hdr);
3793 if (r != ERROR_SUCCESS)
3794 return r;
3796 return ERROR_SUCCESS;
3799 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3801 UINT r;
3802 HKEY source;
3803 LPWSTR buffer;
3804 MSIMEDIADISK *disk;
3805 MSISOURCELISTINFO *info;
3807 r = RegCreateKeyW(hkey, szSourceList, &source);
3808 if (r != ERROR_SUCCESS)
3809 return r;
3811 RegCloseKey(source);
3813 buffer = strrchrW(package->PackagePath, '\\') + 1;
3814 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3815 package->Context, MSICODE_PRODUCT,
3816 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3817 if (r != ERROR_SUCCESS)
3818 return r;
3820 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3821 package->Context, MSICODE_PRODUCT,
3822 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3823 if (r != ERROR_SUCCESS)
3824 return r;
3826 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3827 package->Context, MSICODE_PRODUCT,
3828 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3829 if (r != ERROR_SUCCESS)
3830 return r;
3832 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3834 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3835 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3836 info->options, info->value);
3837 else
3838 MsiSourceListSetInfoW(package->ProductCode, NULL,
3839 info->context, info->options,
3840 info->property, info->value);
3843 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3845 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3846 disk->context, disk->options,
3847 disk->disk_id, disk->volume_label, disk->disk_prompt);
3850 return ERROR_SUCCESS;
3853 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3855 MSIHANDLE hdb, suminfo;
3856 WCHAR guids[MAX_PATH];
3857 WCHAR packcode[SQUISH_GUID_SIZE];
3858 LPWSTR buffer;
3859 LPWSTR ptr;
3860 DWORD langid;
3861 DWORD size;
3862 UINT r;
3864 static const WCHAR szARPProductIcon[] =
3865 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3866 static const WCHAR szAssignment[] =
3867 {'A','s','s','i','g','n','m','e','n','t',0};
3868 static const WCHAR szAdvertiseFlags[] =
3869 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3870 static const WCHAR szClients[] =
3871 {'C','l','i','e','n','t','s',0};
3872 static const WCHAR szColon[] = {':',0};
3874 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3875 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3876 msi_free(buffer);
3878 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3879 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3881 /* FIXME */
3882 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3884 buffer = msi_dup_property(package->db, szARPProductIcon);
3885 if (buffer)
3887 LPWSTR path = msi_build_icon_path(package, buffer);
3888 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3889 msi_free(path);
3890 msi_free(buffer);
3893 buffer = msi_dup_property(package->db, szProductVersion);
3894 if (buffer)
3896 DWORD verdword = msi_version_str_to_dword(buffer);
3897 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3898 msi_free(buffer);
3901 msi_reg_set_val_dword(hkey, szAssignment, 0);
3902 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3903 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3904 msi_reg_set_val_str(hkey, szClients, szColon);
3906 hdb = alloc_msihandle(&package->db->hdr);
3907 if (!hdb)
3908 return ERROR_NOT_ENOUGH_MEMORY;
3910 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3911 MsiCloseHandle(hdb);
3912 if (r != ERROR_SUCCESS)
3913 goto done;
3915 size = MAX_PATH;
3916 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3917 NULL, guids, &size);
3918 if (r != ERROR_SUCCESS)
3919 goto done;
3921 ptr = strchrW(guids, ';');
3922 if (ptr) *ptr = 0;
3923 squash_guid(guids, packcode);
3924 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3926 done:
3927 MsiCloseHandle(suminfo);
3928 return ERROR_SUCCESS;
3931 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3933 UINT r;
3934 HKEY hkey;
3935 LPWSTR upgrade;
3936 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3938 upgrade = msi_dup_property(package->db, szUpgradeCode);
3939 if (!upgrade)
3940 return ERROR_SUCCESS;
3942 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3943 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3944 else
3945 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3947 if (r != ERROR_SUCCESS)
3949 WARN("failed to open upgrade code key\n");
3950 msi_free(upgrade);
3951 return ERROR_SUCCESS;
3953 squash_guid(package->ProductCode, squashed_pc);
3954 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3955 RegCloseKey(hkey);
3956 msi_free(upgrade);
3957 return ERROR_SUCCESS;
3960 static BOOL msi_check_publish(MSIPACKAGE *package)
3962 MSIFEATURE *feature;
3964 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3966 feature->Action = msi_get_feature_action( package, feature );
3967 if (feature->Action == INSTALLSTATE_LOCAL)
3968 return TRUE;
3971 return FALSE;
3974 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3976 MSIFEATURE *feature;
3978 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3980 feature->Action = msi_get_feature_action( package, feature );
3981 if (feature->Action != INSTALLSTATE_ABSENT)
3982 return FALSE;
3985 return TRUE;
3988 static UINT msi_publish_patches( MSIPACKAGE *package )
3990 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3991 WCHAR patch_squashed[GUID_SIZE];
3992 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
3993 LONG res;
3994 MSIPATCHINFO *patch;
3995 UINT r;
3996 WCHAR *p, *all_patches = NULL;
3997 DWORD len = 0;
3999 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4000 if (r != ERROR_SUCCESS)
4001 return ERROR_FUNCTION_FAILED;
4003 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4004 if (res != ERROR_SUCCESS)
4006 r = ERROR_FUNCTION_FAILED;
4007 goto done;
4010 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4011 if (r != ERROR_SUCCESS)
4012 goto done;
4014 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4016 squash_guid( patch->patchcode, patch_squashed );
4017 len += strlenW( patch_squashed ) + 1;
4020 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4021 if (!all_patches)
4022 goto done;
4024 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4026 HKEY patch_key;
4028 squash_guid( patch->patchcode, p );
4029 p += strlenW( p ) + 1;
4031 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4032 (const BYTE *)patch->transforms,
4033 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4034 if (res != ERROR_SUCCESS)
4035 goto done;
4037 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4038 if (r != ERROR_SUCCESS)
4039 goto done;
4041 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4042 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4043 RegCloseKey( patch_key );
4044 if (res != ERROR_SUCCESS)
4045 goto done;
4047 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4049 res = GetLastError();
4050 ERR("Unable to copy patch package %d\n", res);
4051 goto done;
4053 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4054 if (res != ERROR_SUCCESS)
4055 goto done;
4057 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4058 RegCloseKey( patch_key );
4059 if (res != ERROR_SUCCESS)
4060 goto done;
4063 all_patches[len] = 0;
4064 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4065 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4066 if (res != ERROR_SUCCESS)
4067 goto done;
4069 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4070 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4071 if (res != ERROR_SUCCESS)
4072 r = ERROR_FUNCTION_FAILED;
4074 done:
4075 RegCloseKey( product_patches_key );
4076 RegCloseKey( patches_key );
4077 RegCloseKey( product_key );
4078 msi_free( all_patches );
4079 return r;
4082 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4084 UINT rc;
4085 HKEY hukey = NULL, hudkey = NULL;
4086 MSIRECORD *uirow;
4088 if (!list_empty(&package->patches))
4090 rc = msi_publish_patches(package);
4091 if (rc != ERROR_SUCCESS)
4092 goto end;
4095 /* FIXME: also need to publish if the product is in advertise mode */
4096 if (!msi_check_publish(package))
4097 return ERROR_SUCCESS;
4099 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4100 &hukey, TRUE);
4101 if (rc != ERROR_SUCCESS)
4102 goto end;
4104 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4105 NULL, &hudkey, TRUE);
4106 if (rc != ERROR_SUCCESS)
4107 goto end;
4109 rc = msi_publish_upgrade_code(package);
4110 if (rc != ERROR_SUCCESS)
4111 goto end;
4113 rc = msi_publish_product_properties(package, hukey);
4114 if (rc != ERROR_SUCCESS)
4115 goto end;
4117 rc = msi_publish_sourcelist(package, hukey);
4118 if (rc != ERROR_SUCCESS)
4119 goto end;
4121 rc = msi_publish_icons(package);
4123 end:
4124 uirow = MSI_CreateRecord( 1 );
4125 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4126 msi_ui_actiondata( package, szPublishProduct, uirow );
4127 msiobj_release( &uirow->hdr );
4129 RegCloseKey(hukey);
4130 RegCloseKey(hudkey);
4131 return rc;
4134 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4136 WCHAR *filename, *ptr, *folder, *ret;
4137 const WCHAR *dirprop;
4139 filename = msi_dup_record_field( row, 2 );
4140 if (filename && (ptr = strchrW( filename, '|' )))
4141 ptr++;
4142 else
4143 ptr = filename;
4145 dirprop = MSI_RecordGetString( row, 3 );
4146 if (dirprop)
4148 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4149 if (!folder) folder = msi_dup_property( package->db, dirprop );
4151 else
4152 folder = msi_dup_property( package->db, szWindowsFolder );
4154 if (!folder)
4156 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4157 msi_free( filename );
4158 return NULL;
4161 ret = msi_build_directory_name( 2, folder, ptr );
4163 msi_free( filename );
4164 msi_free( folder );
4165 return ret;
4168 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4170 MSIPACKAGE *package = param;
4171 LPCWSTR component, section, key, value, identifier;
4172 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4173 MSIRECORD * uirow;
4174 INT action;
4175 MSICOMPONENT *comp;
4177 component = MSI_RecordGetString(row, 8);
4178 comp = msi_get_loaded_component(package,component);
4179 if (!comp)
4180 return ERROR_SUCCESS;
4182 comp->Action = msi_get_component_action( package, comp );
4183 if (comp->Action != INSTALLSTATE_LOCAL)
4185 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4186 return ERROR_SUCCESS;
4189 identifier = MSI_RecordGetString(row,1);
4190 section = MSI_RecordGetString(row,4);
4191 key = MSI_RecordGetString(row,5);
4192 value = MSI_RecordGetString(row,6);
4193 action = MSI_RecordGetInteger(row,7);
4195 deformat_string(package,section,&deformated_section);
4196 deformat_string(package,key,&deformated_key);
4197 deformat_string(package,value,&deformated_value);
4199 fullname = get_ini_file_name(package, row);
4201 if (action == 0)
4203 TRACE("Adding value %s to section %s in %s\n",
4204 debugstr_w(deformated_key), debugstr_w(deformated_section),
4205 debugstr_w(fullname));
4206 WritePrivateProfileStringW(deformated_section, deformated_key,
4207 deformated_value, fullname);
4209 else if (action == 1)
4211 WCHAR returned[10];
4212 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4213 returned, 10, fullname);
4214 if (returned[0] == 0)
4216 TRACE("Adding value %s to section %s in %s\n",
4217 debugstr_w(deformated_key), debugstr_w(deformated_section),
4218 debugstr_w(fullname));
4220 WritePrivateProfileStringW(deformated_section, deformated_key,
4221 deformated_value, fullname);
4224 else if (action == 3)
4225 FIXME("Append to existing section not yet implemented\n");
4227 uirow = MSI_CreateRecord(4);
4228 MSI_RecordSetStringW(uirow,1,identifier);
4229 MSI_RecordSetStringW(uirow,2,deformated_section);
4230 MSI_RecordSetStringW(uirow,3,deformated_key);
4231 MSI_RecordSetStringW(uirow,4,deformated_value);
4232 msi_ui_actiondata( package, szWriteIniValues, uirow );
4233 msiobj_release( &uirow->hdr );
4235 msi_free(fullname);
4236 msi_free(deformated_key);
4237 msi_free(deformated_value);
4238 msi_free(deformated_section);
4239 return ERROR_SUCCESS;
4242 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4244 static const WCHAR query[] = {
4245 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4246 '`','I','n','i','F','i','l','e','`',0};
4247 MSIQUERY *view;
4248 UINT rc;
4250 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4251 if (rc != ERROR_SUCCESS)
4252 return ERROR_SUCCESS;
4254 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4255 msiobj_release(&view->hdr);
4256 return rc;
4259 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4261 MSIPACKAGE *package = param;
4262 LPCWSTR component, section, key, value, identifier;
4263 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4264 MSICOMPONENT *comp;
4265 MSIRECORD *uirow;
4266 INT action;
4268 component = MSI_RecordGetString( row, 8 );
4269 comp = msi_get_loaded_component( package, component );
4270 if (!comp)
4271 return ERROR_SUCCESS;
4273 comp->Action = msi_get_component_action( package, comp );
4274 if (comp->Action != INSTALLSTATE_ABSENT)
4276 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4277 return ERROR_SUCCESS;
4280 identifier = MSI_RecordGetString( row, 1 );
4281 section = MSI_RecordGetString( row, 4 );
4282 key = MSI_RecordGetString( row, 5 );
4283 value = MSI_RecordGetString( row, 6 );
4284 action = MSI_RecordGetInteger( row, 7 );
4286 deformat_string( package, section, &deformated_section );
4287 deformat_string( package, key, &deformated_key );
4288 deformat_string( package, value, &deformated_value );
4290 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4292 filename = get_ini_file_name( package, row );
4294 TRACE("Removing key %s from section %s in %s\n",
4295 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4297 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4299 WARN("Unable to remove key %u\n", GetLastError());
4301 msi_free( filename );
4303 else
4304 FIXME("Unsupported action %d\n", action);
4307 uirow = MSI_CreateRecord( 4 );
4308 MSI_RecordSetStringW( uirow, 1, identifier );
4309 MSI_RecordSetStringW( uirow, 2, deformated_section );
4310 MSI_RecordSetStringW( uirow, 3, deformated_key );
4311 MSI_RecordSetStringW( uirow, 4, deformated_value );
4312 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4313 msiobj_release( &uirow->hdr );
4315 msi_free( deformated_key );
4316 msi_free( deformated_value );
4317 msi_free( deformated_section );
4318 return ERROR_SUCCESS;
4321 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4323 MSIPACKAGE *package = param;
4324 LPCWSTR component, section, key, value, identifier;
4325 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4326 MSICOMPONENT *comp;
4327 MSIRECORD *uirow;
4328 INT action;
4330 component = MSI_RecordGetString( row, 8 );
4331 comp = msi_get_loaded_component( package, component );
4332 if (!comp)
4333 return ERROR_SUCCESS;
4335 comp->Action = msi_get_component_action( package, comp );
4336 if (comp->Action != INSTALLSTATE_LOCAL)
4338 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4339 return ERROR_SUCCESS;
4342 identifier = MSI_RecordGetString( row, 1 );
4343 section = MSI_RecordGetString( row, 4 );
4344 key = MSI_RecordGetString( row, 5 );
4345 value = MSI_RecordGetString( row, 6 );
4346 action = MSI_RecordGetInteger( row, 7 );
4348 deformat_string( package, section, &deformated_section );
4349 deformat_string( package, key, &deformated_key );
4350 deformat_string( package, value, &deformated_value );
4352 if (action == msidbIniFileActionRemoveLine)
4354 filename = get_ini_file_name( package, row );
4356 TRACE("Removing key %s from section %s in %s\n",
4357 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4359 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4361 WARN("Unable to remove key %u\n", GetLastError());
4363 msi_free( filename );
4365 else
4366 FIXME("Unsupported action %d\n", action);
4368 uirow = MSI_CreateRecord( 4 );
4369 MSI_RecordSetStringW( uirow, 1, identifier );
4370 MSI_RecordSetStringW( uirow, 2, deformated_section );
4371 MSI_RecordSetStringW( uirow, 3, deformated_key );
4372 MSI_RecordSetStringW( uirow, 4, deformated_value );
4373 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4374 msiobj_release( &uirow->hdr );
4376 msi_free( deformated_key );
4377 msi_free( deformated_value );
4378 msi_free( deformated_section );
4379 return ERROR_SUCCESS;
4382 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4384 static const WCHAR query[] = {
4385 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4386 '`','I','n','i','F','i','l','e','`',0};
4387 static const WCHAR remove_query[] = {
4388 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4389 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4390 MSIQUERY *view;
4391 UINT rc;
4393 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4394 if (rc == ERROR_SUCCESS)
4396 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4397 msiobj_release( &view->hdr );
4398 if (rc != ERROR_SUCCESS)
4399 return rc;
4401 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4402 if (rc == ERROR_SUCCESS)
4404 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4405 msiobj_release( &view->hdr );
4406 if (rc != ERROR_SUCCESS)
4407 return rc;
4409 return ERROR_SUCCESS;
4412 static void register_dll( const WCHAR *dll, BOOL unregister )
4414 HMODULE hmod;
4416 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4417 if (hmod)
4419 HRESULT (WINAPI *func_ptr)( void );
4420 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4422 func_ptr = (void *)GetProcAddress( hmod, func );
4423 if (func_ptr)
4425 HRESULT hr = func_ptr();
4426 if (FAILED( hr ))
4427 WARN("failed to register dll 0x%08x\n", hr);
4429 else
4430 WARN("entry point %s not found\n", func);
4431 FreeLibrary( hmod );
4432 return;
4434 WARN("failed to load library %u\n", GetLastError());
4437 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4439 MSIPACKAGE *package = param;
4440 LPCWSTR filename;
4441 MSIFILE *file;
4442 MSIRECORD *uirow;
4444 filename = MSI_RecordGetString( row, 1 );
4445 file = msi_get_loaded_file( package, filename );
4446 if (!file)
4448 WARN("unable to find file %s\n", debugstr_w(filename));
4449 return ERROR_SUCCESS;
4451 file->Component->Action = msi_get_component_action( package, file->Component );
4452 if (file->Component->Action != INSTALLSTATE_LOCAL)
4454 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4455 return ERROR_SUCCESS;
4458 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4459 register_dll( file->TargetPath, FALSE );
4461 uirow = MSI_CreateRecord( 2 );
4462 MSI_RecordSetStringW( uirow, 1, file->File );
4463 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4464 msi_ui_actiondata( package, szSelfRegModules, uirow );
4465 msiobj_release( &uirow->hdr );
4467 return ERROR_SUCCESS;
4470 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4472 static const WCHAR query[] = {
4473 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4474 '`','S','e','l','f','R','e','g','`',0};
4475 MSIQUERY *view;
4476 UINT rc;
4478 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4479 if (rc != ERROR_SUCCESS)
4480 return ERROR_SUCCESS;
4482 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4483 msiobj_release(&view->hdr);
4484 return rc;
4487 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4489 MSIPACKAGE *package = param;
4490 LPCWSTR filename;
4491 MSIFILE *file;
4492 MSIRECORD *uirow;
4494 filename = MSI_RecordGetString( row, 1 );
4495 file = msi_get_loaded_file( package, filename );
4496 if (!file)
4498 WARN("unable to find file %s\n", debugstr_w(filename));
4499 return ERROR_SUCCESS;
4501 file->Component->Action = msi_get_component_action( package, file->Component );
4502 if (file->Component->Action != INSTALLSTATE_ABSENT)
4504 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4505 return ERROR_SUCCESS;
4508 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4509 register_dll( file->TargetPath, TRUE );
4511 uirow = MSI_CreateRecord( 2 );
4512 MSI_RecordSetStringW( uirow, 1, file->File );
4513 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4514 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4515 msiobj_release( &uirow->hdr );
4517 return ERROR_SUCCESS;
4520 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4522 static const WCHAR query[] = {
4523 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4524 '`','S','e','l','f','R','e','g','`',0};
4525 MSIQUERY *view;
4526 UINT rc;
4528 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4529 if (rc != ERROR_SUCCESS)
4530 return ERROR_SUCCESS;
4532 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4533 msiobj_release( &view->hdr );
4534 return rc;
4537 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4539 MSIFEATURE *feature;
4540 UINT rc;
4541 HKEY hkey = NULL, userdata = NULL;
4543 if (!msi_check_publish(package))
4544 return ERROR_SUCCESS;
4546 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4547 &hkey, TRUE);
4548 if (rc != ERROR_SUCCESS)
4549 goto end;
4551 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4552 &userdata, TRUE);
4553 if (rc != ERROR_SUCCESS)
4554 goto end;
4556 /* here the guids are base 85 encoded */
4557 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4559 ComponentList *cl;
4560 LPWSTR data = NULL;
4561 GUID clsid;
4562 INT size;
4563 BOOL absent = FALSE;
4564 MSIRECORD *uirow;
4566 if (feature->Action != INSTALLSTATE_LOCAL &&
4567 feature->Action != INSTALLSTATE_SOURCE &&
4568 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4570 size = 1;
4571 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4573 size += 21;
4575 if (feature->Feature_Parent)
4576 size += strlenW( feature->Feature_Parent )+2;
4578 data = msi_alloc(size * sizeof(WCHAR));
4580 data[0] = 0;
4581 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4583 MSICOMPONENT* component = cl->component;
4584 WCHAR buf[21];
4586 buf[0] = 0;
4587 if (component->ComponentId)
4589 TRACE("From %s\n",debugstr_w(component->ComponentId));
4590 CLSIDFromString(component->ComponentId, &clsid);
4591 encode_base85_guid(&clsid,buf);
4592 TRACE("to %s\n",debugstr_w(buf));
4593 strcatW(data,buf);
4597 if (feature->Feature_Parent)
4599 static const WCHAR sep[] = {'\2',0};
4600 strcatW(data,sep);
4601 strcatW(data,feature->Feature_Parent);
4604 msi_reg_set_val_str( userdata, feature->Feature, data );
4605 msi_free(data);
4607 size = 0;
4608 if (feature->Feature_Parent)
4609 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4610 if (!absent)
4612 size += sizeof(WCHAR);
4613 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4614 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4616 else
4618 size += 2*sizeof(WCHAR);
4619 data = msi_alloc(size);
4620 data[0] = 0x6;
4621 data[1] = 0;
4622 if (feature->Feature_Parent)
4623 strcpyW( &data[1], feature->Feature_Parent );
4624 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4625 (LPBYTE)data,size);
4626 msi_free(data);
4629 /* the UI chunk */
4630 uirow = MSI_CreateRecord( 1 );
4631 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4632 msi_ui_actiondata( package, szPublishFeatures, uirow );
4633 msiobj_release( &uirow->hdr );
4634 /* FIXME: call msi_ui_progress? */
4637 end:
4638 RegCloseKey(hkey);
4639 RegCloseKey(userdata);
4640 return rc;
4643 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4645 UINT r;
4646 HKEY hkey;
4647 MSIRECORD *uirow;
4649 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4651 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4652 &hkey, FALSE);
4653 if (r == ERROR_SUCCESS)
4655 RegDeleteValueW(hkey, feature->Feature);
4656 RegCloseKey(hkey);
4659 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4660 &hkey, FALSE);
4661 if (r == ERROR_SUCCESS)
4663 RegDeleteValueW(hkey, feature->Feature);
4664 RegCloseKey(hkey);
4667 uirow = MSI_CreateRecord( 1 );
4668 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4669 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4670 msiobj_release( &uirow->hdr );
4672 return ERROR_SUCCESS;
4675 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4677 MSIFEATURE *feature;
4679 if (!msi_check_unpublish(package))
4680 return ERROR_SUCCESS;
4682 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4684 msi_unpublish_feature(package, feature);
4687 return ERROR_SUCCESS;
4690 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4692 SYSTEMTIME systime;
4693 DWORD size, langid;
4694 WCHAR date[9], *val, *buffer;
4695 const WCHAR *prop, *key;
4697 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4698 static const WCHAR modpath_fmt[] =
4699 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4700 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4701 static const WCHAR szModifyPath[] =
4702 {'M','o','d','i','f','y','P','a','t','h',0};
4703 static const WCHAR szUninstallString[] =
4704 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4705 static const WCHAR szEstimatedSize[] =
4706 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4707 static const WCHAR szDisplayVersion[] =
4708 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4709 static const WCHAR szInstallSource[] =
4710 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4711 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4712 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4713 static const WCHAR szAuthorizedCDFPrefix[] =
4714 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4715 static const WCHAR szARPCONTACT[] =
4716 {'A','R','P','C','O','N','T','A','C','T',0};
4717 static const WCHAR szContact[] =
4718 {'C','o','n','t','a','c','t',0};
4719 static const WCHAR szARPCOMMENTS[] =
4720 {'A','R','P','C','O','M','M','E','N','T','S',0};
4721 static const WCHAR szComments[] =
4722 {'C','o','m','m','e','n','t','s',0};
4723 static const WCHAR szProductName[] =
4724 {'P','r','o','d','u','c','t','N','a','m','e',0};
4725 static const WCHAR szDisplayName[] =
4726 {'D','i','s','p','l','a','y','N','a','m','e',0};
4727 static const WCHAR szARPHELPLINK[] =
4728 {'A','R','P','H','E','L','P','L','I','N','K',0};
4729 static const WCHAR szHelpLink[] =
4730 {'H','e','l','p','L','i','n','k',0};
4731 static const WCHAR szARPHELPTELEPHONE[] =
4732 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4733 static const WCHAR szHelpTelephone[] =
4734 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4735 static const WCHAR szARPINSTALLLOCATION[] =
4736 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4737 static const WCHAR szInstallLocation[] =
4738 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4739 static const WCHAR szManufacturer[] =
4740 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4741 static const WCHAR szPublisher[] =
4742 {'P','u','b','l','i','s','h','e','r',0};
4743 static const WCHAR szARPREADME[] =
4744 {'A','R','P','R','E','A','D','M','E',0};
4745 static const WCHAR szReadme[] =
4746 {'R','e','a','d','M','e',0};
4747 static const WCHAR szARPSIZE[] =
4748 {'A','R','P','S','I','Z','E',0};
4749 static const WCHAR szSize[] =
4750 {'S','i','z','e',0};
4751 static const WCHAR szARPURLINFOABOUT[] =
4752 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4753 static const WCHAR szURLInfoAbout[] =
4754 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4755 static const WCHAR szARPURLUPDATEINFO[] =
4756 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4757 static const WCHAR szURLUpdateInfo[] =
4758 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4759 static const WCHAR szARPSYSTEMCOMPONENT[] =
4760 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4761 static const WCHAR szSystemComponent[] =
4762 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4764 static const WCHAR *propval[] = {
4765 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4766 szARPCONTACT, szContact,
4767 szARPCOMMENTS, szComments,
4768 szProductName, szDisplayName,
4769 szARPHELPLINK, szHelpLink,
4770 szARPHELPTELEPHONE, szHelpTelephone,
4771 szARPINSTALLLOCATION, szInstallLocation,
4772 szSourceDir, szInstallSource,
4773 szManufacturer, szPublisher,
4774 szARPREADME, szReadme,
4775 szARPSIZE, szSize,
4776 szARPURLINFOABOUT, szURLInfoAbout,
4777 szARPURLUPDATEINFO, szURLUpdateInfo,
4778 NULL
4780 const WCHAR **p = propval;
4782 while (*p)
4784 prop = *p++;
4785 key = *p++;
4786 val = msi_dup_property(package->db, prop);
4787 msi_reg_set_val_str(hkey, key, val);
4788 msi_free(val);
4791 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4792 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4794 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4796 size = deformat_string(package, modpath_fmt, &buffer);
4797 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4798 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4799 msi_free(buffer);
4801 /* FIXME: Write real Estimated Size when we have it */
4802 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4804 GetLocalTime(&systime);
4805 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4806 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4808 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4809 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4811 buffer = msi_dup_property(package->db, szProductVersion);
4812 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4813 if (buffer)
4815 DWORD verdword = msi_version_str_to_dword(buffer);
4817 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4818 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4819 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4820 msi_free(buffer);
4823 return ERROR_SUCCESS;
4826 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4828 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4829 MSIRECORD *uirow;
4830 LPWSTR upgrade_code;
4831 HKEY hkey, props, upgrade_key;
4832 UINT rc;
4834 /* FIXME: also need to publish if the product is in advertise mode */
4835 if (!msi_check_publish(package))
4836 return ERROR_SUCCESS;
4838 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4839 if (rc != ERROR_SUCCESS)
4840 return rc;
4842 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4843 if (rc != ERROR_SUCCESS)
4844 goto done;
4846 rc = msi_publish_install_properties(package, hkey);
4847 if (rc != ERROR_SUCCESS)
4848 goto done;
4850 rc = msi_publish_install_properties(package, props);
4851 if (rc != ERROR_SUCCESS)
4852 goto done;
4854 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4855 if (upgrade_code)
4857 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4858 if (rc == ERROR_SUCCESS)
4860 squash_guid( package->ProductCode, squashed_pc );
4861 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4862 RegCloseKey( upgrade_key );
4864 msi_free( upgrade_code );
4866 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4867 package->delete_on_close = FALSE;
4869 done:
4870 uirow = MSI_CreateRecord( 1 );
4871 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4872 msi_ui_actiondata( package, szRegisterProduct, uirow );
4873 msiobj_release( &uirow->hdr );
4875 RegCloseKey(hkey);
4876 return ERROR_SUCCESS;
4879 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4881 return execute_script(package, SCRIPT_INSTALL);
4884 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4886 MSIPACKAGE *package = param;
4887 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4888 WCHAR *p, *icon_path;
4890 if (!icon) return ERROR_SUCCESS;
4891 if ((icon_path = msi_build_icon_path( package, icon )))
4893 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4894 DeleteFileW( icon_path );
4895 if ((p = strrchrW( icon_path, '\\' )))
4897 *p = 0;
4898 RemoveDirectoryW( icon_path );
4900 msi_free( icon_path );
4902 return ERROR_SUCCESS;
4905 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4907 static const WCHAR query[]= {
4908 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4909 MSIQUERY *view;
4910 UINT r;
4912 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4913 if (r == ERROR_SUCCESS)
4915 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4916 msiobj_release( &view->hdr );
4917 if (r != ERROR_SUCCESS)
4918 return r;
4920 return ERROR_SUCCESS;
4923 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4925 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4926 WCHAR *upgrade, **features;
4927 BOOL full_uninstall = TRUE;
4928 MSIFEATURE *feature;
4929 MSIPATCHINFO *patch;
4930 UINT i;
4932 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4934 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4936 features = msi_split_string( remove, ',' );
4937 for (i = 0; features && features[i]; i++)
4939 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4941 msi_free(features);
4943 if (!full_uninstall)
4944 return ERROR_SUCCESS;
4946 MSIREG_DeleteProductKey(package->ProductCode);
4947 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4948 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4950 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4951 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4952 MSIREG_DeleteUserProductKey(package->ProductCode);
4953 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4955 upgrade = msi_dup_property(package->db, szUpgradeCode);
4956 if (upgrade)
4958 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4959 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4960 msi_free(upgrade);
4963 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4965 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4966 if (!strcmpW( package->ProductCode, patch->products ))
4968 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
4969 patch->delete_on_close = TRUE;
4971 /* FIXME: remove local patch package if this is the last product */
4973 TRACE("removing local package %s\n", debugstr_w(package->localfile));
4974 package->delete_on_close = TRUE;
4976 msi_unpublish_icons( package );
4977 return ERROR_SUCCESS;
4980 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4982 UINT rc;
4983 WCHAR *remove;
4985 /* turn off scheduling */
4986 package->script->CurrentlyScripting= FALSE;
4988 /* first do the same as an InstallExecute */
4989 rc = ACTION_InstallExecute(package);
4990 if (rc != ERROR_SUCCESS)
4991 return rc;
4993 /* then handle commit actions */
4994 rc = execute_script(package, SCRIPT_COMMIT);
4995 if (rc != ERROR_SUCCESS)
4996 return rc;
4998 remove = msi_dup_property(package->db, szRemove);
4999 rc = msi_unpublish_product(package, remove);
5000 msi_free(remove);
5001 return rc;
5004 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5006 static const WCHAR RunOnce[] = {
5007 'S','o','f','t','w','a','r','e','\\',
5008 'M','i','c','r','o','s','o','f','t','\\',
5009 'W','i','n','d','o','w','s','\\',
5010 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5011 'R','u','n','O','n','c','e',0};
5012 static const WCHAR InstallRunOnce[] = {
5013 'S','o','f','t','w','a','r','e','\\',
5014 'M','i','c','r','o','s','o','f','t','\\',
5015 'W','i','n','d','o','w','s','\\',
5016 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5017 'I','n','s','t','a','l','l','e','r','\\',
5018 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5020 static const WCHAR msiexec_fmt[] = {
5021 '%','s',
5022 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5023 '\"','%','s','\"',0};
5024 static const WCHAR install_fmt[] = {
5025 '/','I',' ','\"','%','s','\"',' ',
5026 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5027 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5028 WCHAR buffer[256], sysdir[MAX_PATH];
5029 HKEY hkey;
5030 WCHAR squished_pc[100];
5032 squash_guid(package->ProductCode,squished_pc);
5034 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5035 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5036 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5037 squished_pc);
5039 msi_reg_set_val_str( hkey, squished_pc, buffer );
5040 RegCloseKey(hkey);
5042 TRACE("Reboot command %s\n",debugstr_w(buffer));
5044 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5045 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5047 msi_reg_set_val_str( hkey, squished_pc, buffer );
5048 RegCloseKey(hkey);
5050 return ERROR_INSTALL_SUSPEND;
5053 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5055 static const WCHAR query[] =
5056 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5057 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5058 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5059 MSIRECORD *rec, *row;
5060 DWORD i, size = 0;
5061 va_list va;
5062 const WCHAR *str;
5063 WCHAR *data;
5065 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5067 rec = MSI_CreateRecord( count + 2 );
5068 str = MSI_RecordGetString( row, 1 );
5069 MSI_RecordSetStringW( rec, 0, str );
5070 msiobj_release( &row->hdr );
5071 MSI_RecordSetInteger( rec, 1, error );
5073 va_start( va, count );
5074 for (i = 0; i < count; i++)
5076 str = va_arg( va, const WCHAR *);
5077 MSI_RecordSetStringW( rec, i + 2, str );
5079 va_end( va );
5081 MSI_FormatRecordW( package, rec, NULL, &size );
5082 size++;
5083 data = msi_alloc( size * sizeof(WCHAR) );
5084 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5085 else data[0] = 0;
5086 msiobj_release( &rec->hdr );
5087 return data;
5090 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5092 DWORD attrib;
5093 UINT rc;
5096 * We are currently doing what should be done here in the top level Install
5097 * however for Administrative and uninstalls this step will be needed
5099 if (!package->PackagePath)
5100 return ERROR_SUCCESS;
5102 msi_set_sourcedir_props(package, TRUE);
5104 attrib = GetFileAttributesW(package->db->path);
5105 if (attrib == INVALID_FILE_ATTRIBUTES)
5107 LPWSTR prompt, msg;
5108 DWORD size = 0;
5110 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5111 package->Context, MSICODE_PRODUCT,
5112 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5113 if (rc == ERROR_MORE_DATA)
5115 prompt = msi_alloc(size * sizeof(WCHAR));
5116 MsiSourceListGetInfoW(package->ProductCode, NULL,
5117 package->Context, MSICODE_PRODUCT,
5118 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5120 else
5121 prompt = strdupW(package->db->path);
5123 msg = msi_build_error_string(package, 1302, 1, prompt);
5124 msi_free(prompt);
5125 while(attrib == INVALID_FILE_ATTRIBUTES)
5127 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5128 if (rc == IDCANCEL)
5130 msi_free(msg);
5131 return ERROR_INSTALL_USEREXIT;
5133 attrib = GetFileAttributesW(package->db->path);
5135 msi_free(msg);
5136 rc = ERROR_SUCCESS;
5138 else
5139 return ERROR_SUCCESS;
5141 return rc;
5144 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5146 HKEY hkey = 0;
5147 LPWSTR buffer, productid = NULL;
5148 UINT i, rc = ERROR_SUCCESS;
5149 MSIRECORD *uirow;
5151 static const WCHAR szPropKeys[][80] =
5153 {'P','r','o','d','u','c','t','I','D',0},
5154 {'U','S','E','R','N','A','M','E',0},
5155 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5156 {0},
5159 static const WCHAR szRegKeys[][80] =
5161 {'P','r','o','d','u','c','t','I','D',0},
5162 {'R','e','g','O','w','n','e','r',0},
5163 {'R','e','g','C','o','m','p','a','n','y',0},
5164 {0},
5167 if (msi_check_unpublish(package))
5169 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5170 goto end;
5173 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5174 if (!productid)
5175 goto end;
5177 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5178 NULL, &hkey, TRUE);
5179 if (rc != ERROR_SUCCESS)
5180 goto end;
5182 for( i = 0; szPropKeys[i][0]; i++ )
5184 buffer = msi_dup_property( package->db, szPropKeys[i] );
5185 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5186 msi_free( buffer );
5189 end:
5190 uirow = MSI_CreateRecord( 1 );
5191 MSI_RecordSetStringW( uirow, 1, productid );
5192 msi_ui_actiondata( package, szRegisterUser, uirow );
5193 msiobj_release( &uirow->hdr );
5195 msi_free(productid);
5196 RegCloseKey(hkey);
5197 return rc;
5201 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5203 UINT rc;
5205 package->script->InWhatSequence |= SEQUENCE_EXEC;
5206 rc = ACTION_ProcessExecSequence(package,FALSE);
5207 return rc;
5210 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5212 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5213 WCHAR productid_85[21], component_85[21], *ret;
5214 GUID clsid;
5215 DWORD sz;
5217 /* > is used if there is a component GUID and < if not. */
5219 productid_85[0] = 0;
5220 component_85[0] = 0;
5221 CLSIDFromString( package->ProductCode, &clsid );
5223 encode_base85_guid( &clsid, productid_85 );
5224 if (component)
5226 CLSIDFromString( component->ComponentId, &clsid );
5227 encode_base85_guid( &clsid, component_85 );
5230 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5231 debugstr_w(component_85));
5233 sz = 20 + strlenW( feature ) + 20 + 3;
5234 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5235 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5236 return ret;
5239 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5241 MSIPACKAGE *package = param;
5242 LPCWSTR compgroupid, component, feature, qualifier, text;
5243 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5244 HKEY hkey = NULL;
5245 UINT rc;
5246 MSICOMPONENT *comp;
5247 MSIFEATURE *feat;
5248 DWORD sz;
5249 MSIRECORD *uirow;
5250 int len;
5252 feature = MSI_RecordGetString(rec, 5);
5253 feat = msi_get_loaded_feature(package, feature);
5254 if (!feat)
5255 return ERROR_SUCCESS;
5257 feat->Action = msi_get_feature_action( package, feat );
5258 if (feat->Action != INSTALLSTATE_LOCAL &&
5259 feat->Action != INSTALLSTATE_SOURCE &&
5260 feat->Action != INSTALLSTATE_ADVERTISED)
5262 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5263 return ERROR_SUCCESS;
5266 component = MSI_RecordGetString(rec, 3);
5267 comp = msi_get_loaded_component(package, component);
5268 if (!comp)
5269 return ERROR_SUCCESS;
5271 compgroupid = MSI_RecordGetString(rec,1);
5272 qualifier = MSI_RecordGetString(rec,2);
5274 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5275 if (rc != ERROR_SUCCESS)
5276 goto end;
5278 advertise = msi_create_component_advertise_string( package, comp, feature );
5279 text = MSI_RecordGetString( rec, 4 );
5280 if (text)
5282 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5283 strcpyW( p, advertise );
5284 strcatW( p, text );
5285 msi_free( advertise );
5286 advertise = p;
5288 existing = msi_reg_get_val_str( hkey, qualifier );
5290 sz = strlenW( advertise ) + 1;
5291 if (existing)
5293 for (p = existing; *p; p += len)
5295 len = strlenW( p ) + 1;
5296 if (strcmpW( advertise, p )) sz += len;
5299 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5301 rc = ERROR_OUTOFMEMORY;
5302 goto end;
5304 q = output;
5305 if (existing)
5307 for (p = existing; *p; p += len)
5309 len = strlenW( p ) + 1;
5310 if (strcmpW( advertise, p ))
5312 memcpy( q, p, len * sizeof(WCHAR) );
5313 q += len;
5317 strcpyW( q, advertise );
5318 q[strlenW( q ) + 1] = 0;
5320 msi_reg_set_val_multi_str( hkey, qualifier, output );
5322 end:
5323 RegCloseKey(hkey);
5324 msi_free( output );
5325 msi_free( advertise );
5326 msi_free( existing );
5328 /* the UI chunk */
5329 uirow = MSI_CreateRecord( 2 );
5330 MSI_RecordSetStringW( uirow, 1, compgroupid );
5331 MSI_RecordSetStringW( uirow, 2, qualifier);
5332 msi_ui_actiondata( package, szPublishComponents, uirow );
5333 msiobj_release( &uirow->hdr );
5334 /* FIXME: call ui_progress? */
5336 return rc;
5340 * At present I am ignorning the advertised components part of this and only
5341 * focusing on the qualified component sets
5343 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5345 static const WCHAR query[] = {
5346 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5347 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5348 MSIQUERY *view;
5349 UINT rc;
5351 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5352 if (rc != ERROR_SUCCESS)
5353 return ERROR_SUCCESS;
5355 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5356 msiobj_release(&view->hdr);
5357 return rc;
5360 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5362 static const WCHAR szInstallerComponents[] = {
5363 'S','o','f','t','w','a','r','e','\\',
5364 'M','i','c','r','o','s','o','f','t','\\',
5365 'I','n','s','t','a','l','l','e','r','\\',
5366 'C','o','m','p','o','n','e','n','t','s','\\',0};
5368 MSIPACKAGE *package = param;
5369 LPCWSTR compgroupid, component, feature, qualifier;
5370 MSICOMPONENT *comp;
5371 MSIFEATURE *feat;
5372 MSIRECORD *uirow;
5373 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5374 LONG res;
5376 feature = MSI_RecordGetString( rec, 5 );
5377 feat = msi_get_loaded_feature( package, feature );
5378 if (!feat)
5379 return ERROR_SUCCESS;
5381 feat->Action = msi_get_feature_action( package, feat );
5382 if (feat->Action != INSTALLSTATE_ABSENT)
5384 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5385 return ERROR_SUCCESS;
5388 component = MSI_RecordGetString( rec, 3 );
5389 comp = msi_get_loaded_component( package, component );
5390 if (!comp)
5391 return ERROR_SUCCESS;
5393 compgroupid = MSI_RecordGetString( rec, 1 );
5394 qualifier = MSI_RecordGetString( rec, 2 );
5396 squash_guid( compgroupid, squashed );
5397 strcpyW( keypath, szInstallerComponents );
5398 strcatW( keypath, squashed );
5400 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5401 if (res != ERROR_SUCCESS)
5403 WARN("Unable to delete component key %d\n", res);
5406 uirow = MSI_CreateRecord( 2 );
5407 MSI_RecordSetStringW( uirow, 1, compgroupid );
5408 MSI_RecordSetStringW( uirow, 2, qualifier );
5409 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5410 msiobj_release( &uirow->hdr );
5412 return ERROR_SUCCESS;
5415 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5417 static const WCHAR query[] = {
5418 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5419 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5420 MSIQUERY *view;
5421 UINT rc;
5423 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5424 if (rc != ERROR_SUCCESS)
5425 return ERROR_SUCCESS;
5427 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5428 msiobj_release( &view->hdr );
5429 return rc;
5432 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5434 static const WCHAR query[] =
5435 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5436 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5437 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5438 MSIPACKAGE *package = param;
5439 MSICOMPONENT *component;
5440 MSIRECORD *row;
5441 MSIFILE *file;
5442 SC_HANDLE hscm = NULL, service = NULL;
5443 LPCWSTR comp, key;
5444 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5445 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5446 DWORD serv_type, start_type, err_control;
5447 SERVICE_DESCRIPTIONW sd = {NULL};
5449 comp = MSI_RecordGetString( rec, 12 );
5450 component = msi_get_loaded_component( package, comp );
5451 if (!component)
5453 WARN("service component not found\n");
5454 goto done;
5456 component->Action = msi_get_component_action( package, component );
5457 if (component->Action != INSTALLSTATE_LOCAL)
5459 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5460 goto done;
5462 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5463 if (!hscm)
5465 ERR("Failed to open the SC Manager!\n");
5466 goto done;
5469 start_type = MSI_RecordGetInteger(rec, 5);
5470 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5471 goto done;
5473 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5474 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5475 serv_type = MSI_RecordGetInteger(rec, 4);
5476 err_control = MSI_RecordGetInteger(rec, 6);
5477 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5478 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5479 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5480 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5481 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5482 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5484 /* fetch the service path */
5485 row = MSI_QueryGetRecord(package->db, query, comp);
5486 if (!row)
5488 ERR("Query failed\n");
5489 goto done;
5491 key = MSI_RecordGetString(row, 6);
5492 file = msi_get_loaded_file(package, key);
5493 msiobj_release(&row->hdr);
5494 if (!file)
5496 ERR("Failed to load the service file\n");
5497 goto done;
5500 if (!args || !args[0]) image_path = file->TargetPath;
5501 else
5503 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5504 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5505 return ERROR_OUTOFMEMORY;
5507 strcpyW(image_path, file->TargetPath);
5508 strcatW(image_path, szSpace);
5509 strcatW(image_path, args);
5511 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5512 start_type, err_control, image_path, load_order,
5513 NULL, depends, serv_name, pass);
5515 if (!service)
5517 if (GetLastError() != ERROR_SERVICE_EXISTS)
5518 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5520 else if (sd.lpDescription)
5522 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5523 WARN("failed to set service description %u\n", GetLastError());
5526 if (image_path != file->TargetPath) msi_free(image_path);
5527 done:
5528 CloseServiceHandle(service);
5529 CloseServiceHandle(hscm);
5530 msi_free(name);
5531 msi_free(disp);
5532 msi_free(sd.lpDescription);
5533 msi_free(load_order);
5534 msi_free(serv_name);
5535 msi_free(pass);
5536 msi_free(depends);
5537 msi_free(args);
5539 return ERROR_SUCCESS;
5542 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5544 static const WCHAR query[] = {
5545 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5546 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5547 MSIQUERY *view;
5548 UINT rc;
5550 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5551 if (rc != ERROR_SUCCESS)
5552 return ERROR_SUCCESS;
5554 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5555 msiobj_release(&view->hdr);
5556 return rc;
5559 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5560 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5562 LPCWSTR *vector, *temp_vector;
5563 LPWSTR p, q;
5564 DWORD sep_len;
5566 static const WCHAR separator[] = {'[','~',']',0};
5568 *numargs = 0;
5569 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5571 if (!args)
5572 return NULL;
5574 vector = msi_alloc(sizeof(LPWSTR));
5575 if (!vector)
5576 return NULL;
5578 p = args;
5581 (*numargs)++;
5582 vector[*numargs - 1] = p;
5584 if ((q = strstrW(p, separator)))
5586 *q = '\0';
5588 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5589 if (!temp_vector)
5591 msi_free(vector);
5592 return NULL;
5594 vector = temp_vector;
5596 p = q + sep_len;
5598 } while (q);
5600 return vector;
5603 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5605 MSIPACKAGE *package = param;
5606 MSICOMPONENT *comp;
5607 MSIRECORD *uirow;
5608 SC_HANDLE scm = NULL, service = NULL;
5609 LPCWSTR component, *vector = NULL;
5610 LPWSTR name, args, display_name = NULL;
5611 DWORD event, numargs, len, wait, dummy;
5612 UINT r = ERROR_FUNCTION_FAILED;
5613 SERVICE_STATUS_PROCESS status;
5614 ULONGLONG start_time;
5616 component = MSI_RecordGetString(rec, 6);
5617 comp = msi_get_loaded_component(package, component);
5618 if (!comp)
5619 return ERROR_SUCCESS;
5621 comp->Action = msi_get_component_action( package, comp );
5622 if (comp->Action != INSTALLSTATE_LOCAL)
5624 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5625 return ERROR_SUCCESS;
5628 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5629 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5630 event = MSI_RecordGetInteger(rec, 3);
5631 wait = MSI_RecordGetInteger(rec, 5);
5633 if (!(event & msidbServiceControlEventStart))
5635 r = ERROR_SUCCESS;
5636 goto done;
5639 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5640 if (!scm)
5642 ERR("Failed to open the service control manager\n");
5643 goto done;
5646 len = 0;
5647 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5648 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5650 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5651 GetServiceDisplayNameW( scm, name, display_name, &len );
5654 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5655 if (!service)
5657 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5658 goto done;
5661 vector = msi_service_args_to_vector(args, &numargs);
5663 if (!StartServiceW(service, numargs, vector) &&
5664 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5666 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5667 goto done;
5670 r = ERROR_SUCCESS;
5671 if (wait)
5673 /* wait for at most 30 seconds for the service to be up and running */
5674 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5675 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5677 TRACE("failed to query service status (%u)\n", GetLastError());
5678 goto done;
5680 start_time = GetTickCount64();
5681 while (status.dwCurrentState == SERVICE_START_PENDING)
5683 if (GetTickCount64() - start_time > 30000) break;
5684 Sleep(1000);
5685 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5686 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5688 TRACE("failed to query service status (%u)\n", GetLastError());
5689 goto done;
5692 if (status.dwCurrentState != SERVICE_RUNNING)
5694 WARN("service failed to start %u\n", status.dwCurrentState);
5695 r = ERROR_FUNCTION_FAILED;
5699 done:
5700 uirow = MSI_CreateRecord( 2 );
5701 MSI_RecordSetStringW( uirow, 1, display_name );
5702 MSI_RecordSetStringW( uirow, 2, name );
5703 msi_ui_actiondata( package, szStartServices, uirow );
5704 msiobj_release( &uirow->hdr );
5706 CloseServiceHandle(service);
5707 CloseServiceHandle(scm);
5709 msi_free(name);
5710 msi_free(args);
5711 msi_free(vector);
5712 msi_free(display_name);
5713 return r;
5716 static UINT ACTION_StartServices( MSIPACKAGE *package )
5718 static const WCHAR query[] = {
5719 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5720 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5721 MSIQUERY *view;
5722 UINT rc;
5724 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5725 if (rc != ERROR_SUCCESS)
5726 return ERROR_SUCCESS;
5728 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5729 msiobj_release(&view->hdr);
5730 return rc;
5733 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5735 DWORD i, needed, count;
5736 ENUM_SERVICE_STATUSW *dependencies;
5737 SERVICE_STATUS ss;
5738 SC_HANDLE depserv;
5740 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5741 0, &needed, &count))
5742 return TRUE;
5744 if (GetLastError() != ERROR_MORE_DATA)
5745 return FALSE;
5747 dependencies = msi_alloc(needed);
5748 if (!dependencies)
5749 return FALSE;
5751 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5752 needed, &needed, &count))
5753 goto error;
5755 for (i = 0; i < count; i++)
5757 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5758 SERVICE_STOP | SERVICE_QUERY_STATUS);
5759 if (!depserv)
5760 goto error;
5762 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5763 goto error;
5766 return TRUE;
5768 error:
5769 msi_free(dependencies);
5770 return FALSE;
5773 static UINT stop_service( LPCWSTR name )
5775 SC_HANDLE scm = NULL, service = NULL;
5776 SERVICE_STATUS status;
5777 SERVICE_STATUS_PROCESS ssp;
5778 DWORD needed;
5780 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5781 if (!scm)
5783 WARN("Failed to open the SCM: %d\n", GetLastError());
5784 goto done;
5787 service = OpenServiceW(scm, name,
5788 SERVICE_STOP |
5789 SERVICE_QUERY_STATUS |
5790 SERVICE_ENUMERATE_DEPENDENTS);
5791 if (!service)
5793 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5794 goto done;
5797 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5798 sizeof(SERVICE_STATUS_PROCESS), &needed))
5800 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5801 goto done;
5804 if (ssp.dwCurrentState == SERVICE_STOPPED)
5805 goto done;
5807 stop_service_dependents(scm, service);
5809 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5810 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5812 done:
5813 CloseServiceHandle(service);
5814 CloseServiceHandle(scm);
5816 return ERROR_SUCCESS;
5819 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5821 MSIPACKAGE *package = param;
5822 MSICOMPONENT *comp;
5823 MSIRECORD *uirow;
5824 LPCWSTR component;
5825 LPWSTR name = NULL, display_name = NULL;
5826 DWORD event, len;
5827 SC_HANDLE scm;
5829 event = MSI_RecordGetInteger( rec, 3 );
5830 if (!(event & msidbServiceControlEventStop))
5831 return ERROR_SUCCESS;
5833 component = MSI_RecordGetString( rec, 6 );
5834 comp = msi_get_loaded_component( package, component );
5835 if (!comp)
5836 return ERROR_SUCCESS;
5838 comp->Action = msi_get_component_action( package, comp );
5839 if (comp->Action != INSTALLSTATE_ABSENT)
5841 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5842 return ERROR_SUCCESS;
5845 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5846 if (!scm)
5848 ERR("Failed to open the service control manager\n");
5849 goto done;
5852 len = 0;
5853 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5854 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5856 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5857 GetServiceDisplayNameW( scm, name, display_name, &len );
5859 CloseServiceHandle( scm );
5861 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5862 stop_service( name );
5864 done:
5865 uirow = MSI_CreateRecord( 2 );
5866 MSI_RecordSetStringW( uirow, 1, display_name );
5867 MSI_RecordSetStringW( uirow, 2, name );
5868 msi_ui_actiondata( package, szStopServices, uirow );
5869 msiobj_release( &uirow->hdr );
5871 msi_free( name );
5872 msi_free( display_name );
5873 return ERROR_SUCCESS;
5876 static UINT ACTION_StopServices( MSIPACKAGE *package )
5878 static const WCHAR query[] = {
5879 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5880 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5881 MSIQUERY *view;
5882 UINT rc;
5884 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5885 if (rc != ERROR_SUCCESS)
5886 return ERROR_SUCCESS;
5888 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5889 msiobj_release(&view->hdr);
5890 return rc;
5893 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5895 MSIPACKAGE *package = param;
5896 MSICOMPONENT *comp;
5897 MSIRECORD *uirow;
5898 LPCWSTR component;
5899 LPWSTR name = NULL, display_name = NULL;
5900 DWORD event, len;
5901 SC_HANDLE scm = NULL, service = NULL;
5903 event = MSI_RecordGetInteger( rec, 3 );
5904 if (!(event & msidbServiceControlEventDelete))
5905 return ERROR_SUCCESS;
5907 component = MSI_RecordGetString(rec, 6);
5908 comp = msi_get_loaded_component(package, component);
5909 if (!comp)
5910 return ERROR_SUCCESS;
5912 comp->Action = msi_get_component_action( package, comp );
5913 if (comp->Action != INSTALLSTATE_ABSENT)
5915 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5916 return ERROR_SUCCESS;
5919 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5920 stop_service( name );
5922 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5923 if (!scm)
5925 WARN("Failed to open the SCM: %d\n", GetLastError());
5926 goto done;
5929 len = 0;
5930 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5931 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5933 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5934 GetServiceDisplayNameW( scm, name, display_name, &len );
5937 service = OpenServiceW( scm, name, DELETE );
5938 if (!service)
5940 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5941 goto done;
5944 if (!DeleteService( service ))
5945 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5947 done:
5948 uirow = MSI_CreateRecord( 2 );
5949 MSI_RecordSetStringW( uirow, 1, display_name );
5950 MSI_RecordSetStringW( uirow, 2, name );
5951 msi_ui_actiondata( package, szDeleteServices, uirow );
5952 msiobj_release( &uirow->hdr );
5954 CloseServiceHandle( service );
5955 CloseServiceHandle( scm );
5956 msi_free( name );
5957 msi_free( display_name );
5959 return ERROR_SUCCESS;
5962 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5964 static const WCHAR query[] = {
5965 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5966 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5967 MSIQUERY *view;
5968 UINT rc;
5970 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5971 if (rc != ERROR_SUCCESS)
5972 return ERROR_SUCCESS;
5974 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5975 msiobj_release( &view->hdr );
5976 return rc;
5979 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5981 MSIPACKAGE *package = param;
5982 LPWSTR driver, driver_path, ptr;
5983 WCHAR outpath[MAX_PATH];
5984 MSIFILE *driver_file = NULL, *setup_file = NULL;
5985 MSICOMPONENT *comp;
5986 MSIRECORD *uirow;
5987 LPCWSTR desc, file_key, component;
5988 DWORD len, usage;
5989 UINT r = ERROR_SUCCESS;
5991 static const WCHAR driver_fmt[] = {
5992 'D','r','i','v','e','r','=','%','s',0};
5993 static const WCHAR setup_fmt[] = {
5994 'S','e','t','u','p','=','%','s',0};
5995 static const WCHAR usage_fmt[] = {
5996 'F','i','l','e','U','s','a','g','e','=','1',0};
5998 component = MSI_RecordGetString( rec, 2 );
5999 comp = msi_get_loaded_component( package, component );
6000 if (!comp)
6001 return ERROR_SUCCESS;
6003 comp->Action = msi_get_component_action( package, comp );
6004 if (comp->Action != INSTALLSTATE_LOCAL)
6006 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6007 return ERROR_SUCCESS;
6009 desc = MSI_RecordGetString(rec, 3);
6011 file_key = MSI_RecordGetString( rec, 4 );
6012 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6014 file_key = MSI_RecordGetString( rec, 5 );
6015 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6017 if (!driver_file)
6019 ERR("ODBC Driver entry not found!\n");
6020 return ERROR_FUNCTION_FAILED;
6023 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6024 if (setup_file)
6025 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6026 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6028 driver = msi_alloc(len * sizeof(WCHAR));
6029 if (!driver)
6030 return ERROR_OUTOFMEMORY;
6032 ptr = driver;
6033 lstrcpyW(ptr, desc);
6034 ptr += lstrlenW(ptr) + 1;
6036 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6037 ptr += len + 1;
6039 if (setup_file)
6041 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6042 ptr += len + 1;
6045 lstrcpyW(ptr, usage_fmt);
6046 ptr += lstrlenW(ptr) + 1;
6047 *ptr = '\0';
6049 driver_path = strdupW(driver_file->TargetPath);
6050 ptr = strrchrW(driver_path, '\\');
6051 if (ptr) *ptr = '\0';
6053 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6054 NULL, ODBC_INSTALL_COMPLETE, &usage))
6056 ERR("Failed to install SQL driver!\n");
6057 r = ERROR_FUNCTION_FAILED;
6060 uirow = MSI_CreateRecord( 5 );
6061 MSI_RecordSetStringW( uirow, 1, desc );
6062 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6063 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6064 msi_ui_actiondata( package, szInstallODBC, uirow );
6065 msiobj_release( &uirow->hdr );
6067 msi_free(driver);
6068 msi_free(driver_path);
6070 return r;
6073 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6075 MSIPACKAGE *package = param;
6076 LPWSTR translator, translator_path, ptr;
6077 WCHAR outpath[MAX_PATH];
6078 MSIFILE *translator_file = NULL, *setup_file = NULL;
6079 MSICOMPONENT *comp;
6080 MSIRECORD *uirow;
6081 LPCWSTR desc, file_key, component;
6082 DWORD len, usage;
6083 UINT r = ERROR_SUCCESS;
6085 static const WCHAR translator_fmt[] = {
6086 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6087 static const WCHAR setup_fmt[] = {
6088 'S','e','t','u','p','=','%','s',0};
6090 component = MSI_RecordGetString( rec, 2 );
6091 comp = msi_get_loaded_component( package, component );
6092 if (!comp)
6093 return ERROR_SUCCESS;
6095 comp->Action = msi_get_component_action( package, comp );
6096 if (comp->Action != INSTALLSTATE_LOCAL)
6098 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6099 return ERROR_SUCCESS;
6101 desc = MSI_RecordGetString(rec, 3);
6103 file_key = MSI_RecordGetString( rec, 4 );
6104 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6106 file_key = MSI_RecordGetString( rec, 5 );
6107 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6109 if (!translator_file)
6111 ERR("ODBC Translator entry not found!\n");
6112 return ERROR_FUNCTION_FAILED;
6115 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6116 if (setup_file)
6117 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6119 translator = msi_alloc(len * sizeof(WCHAR));
6120 if (!translator)
6121 return ERROR_OUTOFMEMORY;
6123 ptr = translator;
6124 lstrcpyW(ptr, desc);
6125 ptr += lstrlenW(ptr) + 1;
6127 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6128 ptr += len + 1;
6130 if (setup_file)
6132 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6133 ptr += len + 1;
6135 *ptr = '\0';
6137 translator_path = strdupW(translator_file->TargetPath);
6138 ptr = strrchrW(translator_path, '\\');
6139 if (ptr) *ptr = '\0';
6141 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6142 NULL, ODBC_INSTALL_COMPLETE, &usage))
6144 ERR("Failed to install SQL translator!\n");
6145 r = ERROR_FUNCTION_FAILED;
6148 uirow = MSI_CreateRecord( 5 );
6149 MSI_RecordSetStringW( uirow, 1, desc );
6150 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6151 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6152 msi_ui_actiondata( package, szInstallODBC, uirow );
6153 msiobj_release( &uirow->hdr );
6155 msi_free(translator);
6156 msi_free(translator_path);
6158 return r;
6161 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6163 MSIPACKAGE *package = param;
6164 MSICOMPONENT *comp;
6165 LPWSTR attrs;
6166 LPCWSTR desc, driver, component;
6167 WORD request = ODBC_ADD_SYS_DSN;
6168 INT registration;
6169 DWORD len;
6170 UINT r = ERROR_SUCCESS;
6171 MSIRECORD *uirow;
6173 static const WCHAR attrs_fmt[] = {
6174 'D','S','N','=','%','s',0 };
6176 component = MSI_RecordGetString( rec, 2 );
6177 comp = msi_get_loaded_component( package, component );
6178 if (!comp)
6179 return ERROR_SUCCESS;
6181 comp->Action = msi_get_component_action( package, comp );
6182 if (comp->Action != INSTALLSTATE_LOCAL)
6184 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6185 return ERROR_SUCCESS;
6188 desc = MSI_RecordGetString(rec, 3);
6189 driver = MSI_RecordGetString(rec, 4);
6190 registration = MSI_RecordGetInteger(rec, 5);
6192 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6193 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6195 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6196 attrs = msi_alloc(len * sizeof(WCHAR));
6197 if (!attrs)
6198 return ERROR_OUTOFMEMORY;
6200 len = sprintfW(attrs, attrs_fmt, desc);
6201 attrs[len + 1] = 0;
6203 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6205 ERR("Failed to install SQL data source!\n");
6206 r = ERROR_FUNCTION_FAILED;
6209 uirow = MSI_CreateRecord( 5 );
6210 MSI_RecordSetStringW( uirow, 1, desc );
6211 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6212 MSI_RecordSetInteger( uirow, 3, request );
6213 msi_ui_actiondata( package, szInstallODBC, uirow );
6214 msiobj_release( &uirow->hdr );
6216 msi_free(attrs);
6218 return r;
6221 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6223 static const WCHAR driver_query[] = {
6224 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6225 'O','D','B','C','D','r','i','v','e','r',0};
6226 static const WCHAR translator_query[] = {
6227 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6228 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6229 static const WCHAR source_query[] = {
6230 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6231 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6232 MSIQUERY *view;
6233 UINT rc;
6235 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6236 if (rc == ERROR_SUCCESS)
6238 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6239 msiobj_release(&view->hdr);
6240 if (rc != ERROR_SUCCESS)
6241 return rc;
6243 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6244 if (rc == ERROR_SUCCESS)
6246 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6247 msiobj_release(&view->hdr);
6248 if (rc != ERROR_SUCCESS)
6249 return rc;
6251 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6252 if (rc == ERROR_SUCCESS)
6254 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6255 msiobj_release(&view->hdr);
6256 if (rc != ERROR_SUCCESS)
6257 return rc;
6259 return ERROR_SUCCESS;
6262 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6264 MSIPACKAGE *package = param;
6265 MSICOMPONENT *comp;
6266 MSIRECORD *uirow;
6267 DWORD usage;
6268 LPCWSTR desc, component;
6270 component = MSI_RecordGetString( rec, 2 );
6271 comp = msi_get_loaded_component( package, component );
6272 if (!comp)
6273 return ERROR_SUCCESS;
6275 comp->Action = msi_get_component_action( package, comp );
6276 if (comp->Action != INSTALLSTATE_ABSENT)
6278 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6279 return ERROR_SUCCESS;
6282 desc = MSI_RecordGetString( rec, 3 );
6283 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6285 WARN("Failed to remove ODBC driver\n");
6287 else if (!usage)
6289 FIXME("Usage count reached 0\n");
6292 uirow = MSI_CreateRecord( 2 );
6293 MSI_RecordSetStringW( uirow, 1, desc );
6294 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6295 msi_ui_actiondata( package, szRemoveODBC, uirow );
6296 msiobj_release( &uirow->hdr );
6298 return ERROR_SUCCESS;
6301 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6303 MSIPACKAGE *package = param;
6304 MSICOMPONENT *comp;
6305 MSIRECORD *uirow;
6306 DWORD usage;
6307 LPCWSTR desc, component;
6309 component = MSI_RecordGetString( rec, 2 );
6310 comp = msi_get_loaded_component( package, component );
6311 if (!comp)
6312 return ERROR_SUCCESS;
6314 comp->Action = msi_get_component_action( package, comp );
6315 if (comp->Action != INSTALLSTATE_ABSENT)
6317 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6318 return ERROR_SUCCESS;
6321 desc = MSI_RecordGetString( rec, 3 );
6322 if (!SQLRemoveTranslatorW( desc, &usage ))
6324 WARN("Failed to remove ODBC translator\n");
6326 else if (!usage)
6328 FIXME("Usage count reached 0\n");
6331 uirow = MSI_CreateRecord( 2 );
6332 MSI_RecordSetStringW( uirow, 1, desc );
6333 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6334 msi_ui_actiondata( package, szRemoveODBC, uirow );
6335 msiobj_release( &uirow->hdr );
6337 return ERROR_SUCCESS;
6340 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6342 MSIPACKAGE *package = param;
6343 MSICOMPONENT *comp;
6344 MSIRECORD *uirow;
6345 LPWSTR attrs;
6346 LPCWSTR desc, driver, component;
6347 WORD request = ODBC_REMOVE_SYS_DSN;
6348 INT registration;
6349 DWORD len;
6351 static const WCHAR attrs_fmt[] = {
6352 'D','S','N','=','%','s',0 };
6354 component = MSI_RecordGetString( rec, 2 );
6355 comp = msi_get_loaded_component( package, component );
6356 if (!comp)
6357 return ERROR_SUCCESS;
6359 comp->Action = msi_get_component_action( package, comp );
6360 if (comp->Action != INSTALLSTATE_ABSENT)
6362 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6363 return ERROR_SUCCESS;
6366 desc = MSI_RecordGetString( rec, 3 );
6367 driver = MSI_RecordGetString( rec, 4 );
6368 registration = MSI_RecordGetInteger( rec, 5 );
6370 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6371 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6373 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6374 attrs = msi_alloc( len * sizeof(WCHAR) );
6375 if (!attrs)
6376 return ERROR_OUTOFMEMORY;
6378 FIXME("Use ODBCSourceAttribute table\n");
6380 len = sprintfW( attrs, attrs_fmt, desc );
6381 attrs[len + 1] = 0;
6383 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6385 WARN("Failed to remove ODBC data source\n");
6387 msi_free( attrs );
6389 uirow = MSI_CreateRecord( 3 );
6390 MSI_RecordSetStringW( uirow, 1, desc );
6391 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6392 MSI_RecordSetInteger( uirow, 3, request );
6393 msi_ui_actiondata( package, szRemoveODBC, uirow );
6394 msiobj_release( &uirow->hdr );
6396 return ERROR_SUCCESS;
6399 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6401 static const WCHAR driver_query[] = {
6402 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6403 'O','D','B','C','D','r','i','v','e','r',0};
6404 static const WCHAR translator_query[] = {
6405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6406 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6407 static const WCHAR source_query[] = {
6408 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6409 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6410 MSIQUERY *view;
6411 UINT rc;
6413 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6414 if (rc == ERROR_SUCCESS)
6416 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6417 msiobj_release( &view->hdr );
6418 if (rc != ERROR_SUCCESS)
6419 return rc;
6421 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6422 if (rc == ERROR_SUCCESS)
6424 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6425 msiobj_release( &view->hdr );
6426 if (rc != ERROR_SUCCESS)
6427 return rc;
6429 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6430 if (rc == ERROR_SUCCESS)
6432 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6433 msiobj_release( &view->hdr );
6434 if (rc != ERROR_SUCCESS)
6435 return rc;
6437 return ERROR_SUCCESS;
6440 #define ENV_ACT_SETALWAYS 0x1
6441 #define ENV_ACT_SETABSENT 0x2
6442 #define ENV_ACT_REMOVE 0x4
6443 #define ENV_ACT_REMOVEMATCH 0x8
6445 #define ENV_MOD_MACHINE 0x20000000
6446 #define ENV_MOD_APPEND 0x40000000
6447 #define ENV_MOD_PREFIX 0x80000000
6448 #define ENV_MOD_MASK 0xC0000000
6450 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6452 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6454 LPCWSTR cptr = *name;
6456 static const WCHAR prefix[] = {'[','~',']',0};
6457 static const int prefix_len = 3;
6459 *flags = 0;
6460 while (*cptr)
6462 if (*cptr == '=')
6463 *flags |= ENV_ACT_SETALWAYS;
6464 else if (*cptr == '+')
6465 *flags |= ENV_ACT_SETABSENT;
6466 else if (*cptr == '-')
6467 *flags |= ENV_ACT_REMOVE;
6468 else if (*cptr == '!')
6469 *flags |= ENV_ACT_REMOVEMATCH;
6470 else if (*cptr == '*')
6471 *flags |= ENV_MOD_MACHINE;
6472 else
6473 break;
6475 cptr++;
6476 (*name)++;
6479 if (!*cptr)
6481 ERR("Missing environment variable\n");
6482 return ERROR_FUNCTION_FAILED;
6485 if (*value)
6487 LPCWSTR ptr = *value;
6488 if (!strncmpW(ptr, prefix, prefix_len))
6490 if (ptr[prefix_len] == szSemiColon[0])
6492 *flags |= ENV_MOD_APPEND;
6493 *value += lstrlenW(prefix);
6495 else
6497 *value = NULL;
6500 else if (lstrlenW(*value) >= prefix_len)
6502 ptr += lstrlenW(ptr) - prefix_len;
6503 if (!strcmpW( ptr, prefix ))
6505 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6507 *flags |= ENV_MOD_PREFIX;
6508 /* the "[~]" will be removed by deformat_string */;
6510 else
6512 *value = NULL;
6518 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6519 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6520 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6521 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6523 ERR("Invalid flags: %08x\n", *flags);
6524 return ERROR_FUNCTION_FAILED;
6527 if (!*flags)
6528 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6530 return ERROR_SUCCESS;
6533 static UINT open_env_key( DWORD flags, HKEY *key )
6535 static const WCHAR user_env[] =
6536 {'E','n','v','i','r','o','n','m','e','n','t',0};
6537 static const WCHAR machine_env[] =
6538 {'S','y','s','t','e','m','\\',
6539 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6540 'C','o','n','t','r','o','l','\\',
6541 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6542 'E','n','v','i','r','o','n','m','e','n','t',0};
6543 const WCHAR *env;
6544 HKEY root;
6545 LONG res;
6547 if (flags & ENV_MOD_MACHINE)
6549 env = machine_env;
6550 root = HKEY_LOCAL_MACHINE;
6552 else
6554 env = user_env;
6555 root = HKEY_CURRENT_USER;
6558 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6559 if (res != ERROR_SUCCESS)
6561 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6562 return ERROR_FUNCTION_FAILED;
6565 return ERROR_SUCCESS;
6568 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6570 MSIPACKAGE *package = param;
6571 LPCWSTR name, value, component;
6572 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6573 DWORD flags, type, size;
6574 UINT res;
6575 HKEY env = NULL;
6576 MSICOMPONENT *comp;
6577 MSIRECORD *uirow;
6578 int action = 0;
6580 component = MSI_RecordGetString(rec, 4);
6581 comp = msi_get_loaded_component(package, component);
6582 if (!comp)
6583 return ERROR_SUCCESS;
6585 comp->Action = msi_get_component_action( package, comp );
6586 if (comp->Action != INSTALLSTATE_LOCAL)
6588 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6589 return ERROR_SUCCESS;
6591 name = MSI_RecordGetString(rec, 2);
6592 value = MSI_RecordGetString(rec, 3);
6594 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6596 res = env_parse_flags(&name, &value, &flags);
6597 if (res != ERROR_SUCCESS || !value)
6598 goto done;
6600 if (value && !deformat_string(package, value, &deformatted))
6602 res = ERROR_OUTOFMEMORY;
6603 goto done;
6606 value = deformatted;
6608 res = open_env_key( flags, &env );
6609 if (res != ERROR_SUCCESS)
6610 goto done;
6612 if (flags & ENV_MOD_MACHINE)
6613 action |= 0x20000000;
6615 size = 0;
6616 type = REG_SZ;
6617 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6618 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6619 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6620 goto done;
6622 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6624 action = 0x2;
6626 /* Nothing to do. */
6627 if (!value)
6629 res = ERROR_SUCCESS;
6630 goto done;
6633 /* If we are appending but the string was empty, strip ; */
6634 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6636 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6637 newval = strdupW(value);
6638 if (!newval)
6640 res = ERROR_OUTOFMEMORY;
6641 goto done;
6644 else
6646 action = 0x1;
6648 /* Contrary to MSDN, +-variable to [~];path works */
6649 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6651 res = ERROR_SUCCESS;
6652 goto done;
6655 data = msi_alloc(size);
6656 if (!data)
6658 RegCloseKey(env);
6659 return ERROR_OUTOFMEMORY;
6662 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6663 if (res != ERROR_SUCCESS)
6664 goto done;
6666 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6668 action = 0x4;
6669 res = RegDeleteValueW(env, name);
6670 if (res != ERROR_SUCCESS)
6671 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6672 goto done;
6675 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6676 if (flags & ENV_MOD_MASK)
6678 DWORD mod_size;
6679 int multiplier = 0;
6680 if (flags & ENV_MOD_APPEND) multiplier++;
6681 if (flags & ENV_MOD_PREFIX) multiplier++;
6682 mod_size = lstrlenW(value) * multiplier;
6683 size += mod_size * sizeof(WCHAR);
6686 newval = msi_alloc(size);
6687 ptr = newval;
6688 if (!newval)
6690 res = ERROR_OUTOFMEMORY;
6691 goto done;
6694 if (flags & ENV_MOD_PREFIX)
6696 lstrcpyW(newval, value);
6697 ptr = newval + lstrlenW(value);
6698 action |= 0x80000000;
6701 lstrcpyW(ptr, data);
6703 if (flags & ENV_MOD_APPEND)
6705 lstrcatW(newval, value);
6706 action |= 0x40000000;
6709 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6710 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6711 if (res)
6713 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6716 done:
6717 uirow = MSI_CreateRecord( 3 );
6718 MSI_RecordSetStringW( uirow, 1, name );
6719 MSI_RecordSetStringW( uirow, 2, newval );
6720 MSI_RecordSetInteger( uirow, 3, action );
6721 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6722 msiobj_release( &uirow->hdr );
6724 if (env) RegCloseKey(env);
6725 msi_free(deformatted);
6726 msi_free(data);
6727 msi_free(newval);
6728 return res;
6731 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6733 static const WCHAR query[] = {
6734 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6735 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6736 MSIQUERY *view;
6737 UINT rc;
6739 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6740 if (rc != ERROR_SUCCESS)
6741 return ERROR_SUCCESS;
6743 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6744 msiobj_release(&view->hdr);
6745 return rc;
6748 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6750 MSIPACKAGE *package = param;
6751 LPCWSTR name, value, component;
6752 LPWSTR deformatted = NULL;
6753 DWORD flags;
6754 HKEY env;
6755 MSICOMPONENT *comp;
6756 MSIRECORD *uirow;
6757 int action = 0;
6758 LONG res;
6759 UINT r;
6761 component = MSI_RecordGetString( rec, 4 );
6762 comp = msi_get_loaded_component( package, component );
6763 if (!comp)
6764 return ERROR_SUCCESS;
6766 comp->Action = msi_get_component_action( package, comp );
6767 if (comp->Action != INSTALLSTATE_ABSENT)
6769 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6770 return ERROR_SUCCESS;
6772 name = MSI_RecordGetString( rec, 2 );
6773 value = MSI_RecordGetString( rec, 3 );
6775 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6777 r = env_parse_flags( &name, &value, &flags );
6778 if (r != ERROR_SUCCESS)
6779 return r;
6781 if (!(flags & ENV_ACT_REMOVE))
6783 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6784 return ERROR_SUCCESS;
6787 if (value && !deformat_string( package, value, &deformatted ))
6788 return ERROR_OUTOFMEMORY;
6790 value = deformatted;
6792 r = open_env_key( flags, &env );
6793 if (r != ERROR_SUCCESS)
6795 r = ERROR_SUCCESS;
6796 goto done;
6799 if (flags & ENV_MOD_MACHINE)
6800 action |= 0x20000000;
6802 TRACE("Removing %s\n", debugstr_w(name));
6804 res = RegDeleteValueW( env, name );
6805 if (res != ERROR_SUCCESS)
6807 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6808 r = ERROR_SUCCESS;
6811 done:
6812 uirow = MSI_CreateRecord( 3 );
6813 MSI_RecordSetStringW( uirow, 1, name );
6814 MSI_RecordSetStringW( uirow, 2, value );
6815 MSI_RecordSetInteger( uirow, 3, action );
6816 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6817 msiobj_release( &uirow->hdr );
6819 if (env) RegCloseKey( env );
6820 msi_free( deformatted );
6821 return r;
6824 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6826 static const WCHAR query[] = {
6827 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6828 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6829 MSIQUERY *view;
6830 UINT rc;
6832 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6833 if (rc != ERROR_SUCCESS)
6834 return ERROR_SUCCESS;
6836 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6837 msiobj_release( &view->hdr );
6838 return rc;
6841 UINT msi_validate_product_id( MSIPACKAGE *package )
6843 LPWSTR key, template, id;
6844 UINT r = ERROR_SUCCESS;
6846 id = msi_dup_property( package->db, szProductID );
6847 if (id)
6849 msi_free( id );
6850 return ERROR_SUCCESS;
6852 template = msi_dup_property( package->db, szPIDTemplate );
6853 key = msi_dup_property( package->db, szPIDKEY );
6854 if (key && template)
6856 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6857 r = msi_set_property( package->db, szProductID, key );
6859 msi_free( template );
6860 msi_free( key );
6861 return r;
6864 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6866 return msi_validate_product_id( package );
6869 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6871 TRACE("\n");
6872 package->need_reboot = 1;
6873 return ERROR_SUCCESS;
6876 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6878 static const WCHAR szAvailableFreeReg[] =
6879 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6880 MSIRECORD *uirow;
6881 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6883 TRACE("%p %d kilobytes\n", package, space);
6885 uirow = MSI_CreateRecord( 1 );
6886 MSI_RecordSetInteger( uirow, 1, space );
6887 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6888 msiobj_release( &uirow->hdr );
6890 return ERROR_SUCCESS;
6893 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6895 TRACE("%p\n", package);
6897 msi_set_property( package->db, szRollbackDisabled, szOne );
6898 return ERROR_SUCCESS;
6901 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6903 FIXME("%p\n", package);
6904 return ERROR_SUCCESS;
6907 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6909 static const WCHAR driver_query[] = {
6910 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6911 'O','D','B','C','D','r','i','v','e','r',0};
6912 static const WCHAR translator_query[] = {
6913 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6914 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6915 MSIQUERY *view;
6916 UINT r, count;
6918 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6919 if (r == ERROR_SUCCESS)
6921 count = 0;
6922 r = MSI_IterateRecords( view, &count, NULL, package );
6923 msiobj_release( &view->hdr );
6924 if (r != ERROR_SUCCESS)
6925 return r;
6926 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6928 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6929 if (r == ERROR_SUCCESS)
6931 count = 0;
6932 r = MSI_IterateRecords( view, &count, NULL, package );
6933 msiobj_release( &view->hdr );
6934 if (r != ERROR_SUCCESS)
6935 return r;
6936 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6938 return ERROR_SUCCESS;
6941 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6943 MSIPACKAGE *package = param;
6944 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6945 WCHAR *value;
6947 if ((value = msi_dup_property( package->db, property )))
6949 FIXME("remove %s\n", debugstr_w(value));
6950 msi_free( value );
6952 return ERROR_SUCCESS;
6955 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6957 static const WCHAR query[] = {
6958 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6959 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6960 MSIQUERY *view;
6961 UINT r;
6963 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6964 if (r == ERROR_SUCCESS)
6966 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6967 msiobj_release( &view->hdr );
6968 if (r != ERROR_SUCCESS)
6969 return r;
6971 return ERROR_SUCCESS;
6974 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6976 MSIPACKAGE *package = param;
6977 int attributes = MSI_RecordGetInteger( rec, 5 );
6979 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6981 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6982 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6983 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6984 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6985 HKEY hkey;
6986 UINT r;
6988 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
6990 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6991 if (r != ERROR_SUCCESS)
6992 return ERROR_SUCCESS;
6994 else
6996 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6997 if (r != ERROR_SUCCESS)
6998 return ERROR_SUCCESS;
7000 RegCloseKey( hkey );
7002 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7003 debugstr_w(upgrade_code), debugstr_w(version_min),
7004 debugstr_w(version_max), debugstr_w(language));
7006 return ERROR_SUCCESS;
7009 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7011 static const WCHAR query[] = {
7012 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7013 'U','p','g','r','a','d','e',0};
7014 MSIQUERY *view;
7015 UINT r;
7017 if (msi_get_property_int( package->db, szInstalled, 0 ))
7019 TRACE("product is installed, skipping action\n");
7020 return ERROR_SUCCESS;
7022 if (msi_get_property_int( package->db, szPreselected, 0 ))
7024 TRACE("Preselected property is set, not migrating feature states\n");
7025 return ERROR_SUCCESS;
7027 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7028 if (r == ERROR_SUCCESS)
7030 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7031 msiobj_release( &view->hdr );
7032 if (r != ERROR_SUCCESS)
7033 return r;
7035 return ERROR_SUCCESS;
7038 static void bind_image( const char *filename, const char *path )
7040 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7042 WARN("failed to bind image %u\n", GetLastError());
7046 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7048 UINT i;
7049 MSIFILE *file;
7050 MSIPACKAGE *package = param;
7051 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7052 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7053 char *filenameA, *pathA;
7054 WCHAR *pathW, **path_list;
7056 if (!(file = msi_get_loaded_file( package, key )))
7058 WARN("file %s not found\n", debugstr_w(key));
7059 return ERROR_SUCCESS;
7061 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7062 path_list = msi_split_string( paths, ';' );
7063 if (!path_list) bind_image( filenameA, NULL );
7064 else
7066 for (i = 0; path_list[i] && path_list[i][0]; i++)
7068 deformat_string( package, path_list[i], &pathW );
7069 if ((pathA = strdupWtoA( pathW )))
7071 bind_image( filenameA, pathA );
7072 msi_free( pathA );
7074 msi_free( pathW );
7077 msi_free( path_list );
7078 msi_free( filenameA );
7079 return ERROR_SUCCESS;
7082 static UINT ACTION_BindImage( MSIPACKAGE *package )
7084 static const WCHAR query[] = {
7085 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7086 'B','i','n','d','I','m','a','g','e',0};
7087 MSIQUERY *view;
7088 UINT r;
7090 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7091 if (r == ERROR_SUCCESS)
7093 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7094 msiobj_release( &view->hdr );
7095 if (r != ERROR_SUCCESS)
7096 return r;
7098 return ERROR_SUCCESS;
7101 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7103 static const WCHAR query[] = {
7104 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7105 MSIQUERY *view;
7106 DWORD count = 0;
7107 UINT r;
7109 r = MSI_OpenQuery( package->db, &view, query, table );
7110 if (r == ERROR_SUCCESS)
7112 r = MSI_IterateRecords(view, &count, NULL, package);
7113 msiobj_release(&view->hdr);
7114 if (r != ERROR_SUCCESS)
7115 return r;
7117 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7118 return ERROR_SUCCESS;
7121 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7123 static const WCHAR table[] = {
7124 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7125 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7128 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7130 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7131 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7134 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7136 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7137 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7140 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7142 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7143 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7146 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7148 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7149 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7152 static const struct
7154 const WCHAR *action;
7155 UINT (*handler)(MSIPACKAGE *);
7156 const WCHAR *action_rollback;
7158 StandardActions[] =
7160 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7161 { szAppSearch, ACTION_AppSearch, NULL },
7162 { szBindImage, ACTION_BindImage, NULL },
7163 { szCCPSearch, ACTION_CCPSearch, NULL },
7164 { szCostFinalize, ACTION_CostFinalize, NULL },
7165 { szCostInitialize, ACTION_CostInitialize, NULL },
7166 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7167 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7168 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7169 { szDisableRollback, ACTION_DisableRollback, NULL },
7170 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7171 { szExecuteAction, ACTION_ExecuteAction, NULL },
7172 { szFileCost, ACTION_FileCost, NULL },
7173 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7174 { szForceReboot, ACTION_ForceReboot, NULL },
7175 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7176 { szInstallExecute, ACTION_InstallExecute, NULL },
7177 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7178 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7179 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7180 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7181 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7182 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7183 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7184 { szInstallValidate, ACTION_InstallValidate, NULL },
7185 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7186 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7187 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7188 { szMoveFiles, ACTION_MoveFiles, NULL },
7189 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7190 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7191 { szPatchFiles, ACTION_PatchFiles, NULL },
7192 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7193 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7194 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7195 { szPublishProduct, ACTION_PublishProduct, NULL },
7196 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7197 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7198 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7199 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7200 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7201 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7202 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7203 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7204 { szRegisterUser, ACTION_RegisterUser, NULL },
7205 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7206 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7207 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7208 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7209 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7210 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7211 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7212 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7213 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7214 { szResolveSource, ACTION_ResolveSource, NULL },
7215 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7216 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7217 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7218 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7219 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7220 { szStartServices, ACTION_StartServices, szStopServices },
7221 { szStopServices, ACTION_StopServices, szStartServices },
7222 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7223 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7224 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7225 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7226 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7227 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7228 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7229 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7230 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7231 { szValidateProductID, ACTION_ValidateProductID, NULL },
7232 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7233 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7234 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7235 { NULL, NULL, NULL }
7238 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7240 BOOL ret = FALSE;
7241 UINT i;
7243 i = 0;
7244 while (StandardActions[i].action != NULL)
7246 if (!strcmpW( StandardActions[i].action, action ))
7248 ui_actionstart( package, action );
7249 if (StandardActions[i].handler)
7251 ui_actioninfo( package, action, TRUE, 0 );
7252 *rc = StandardActions[i].handler( package );
7253 ui_actioninfo( package, action, FALSE, *rc );
7255 if (StandardActions[i].action_rollback && !package->need_rollback)
7257 TRACE("scheduling rollback action\n");
7258 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7261 else
7263 FIXME("unhandled standard action %s\n", debugstr_w(action));
7264 *rc = ERROR_SUCCESS;
7266 ret = TRUE;
7267 break;
7269 i++;
7271 return ret;
7274 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7276 UINT rc = ERROR_SUCCESS;
7277 BOOL handled;
7279 TRACE("Performing action (%s)\n", debugstr_w(action));
7281 handled = ACTION_HandleStandardAction(package, action, &rc);
7283 if (!handled)
7284 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7286 if (!handled)
7288 WARN("unhandled msi action %s\n", debugstr_w(action));
7289 rc = ERROR_FUNCTION_NOT_CALLED;
7292 return rc;
7295 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7297 UINT rc = ERROR_SUCCESS;
7298 BOOL handled = FALSE;
7300 TRACE("Performing action (%s)\n", debugstr_w(action));
7302 handled = ACTION_HandleStandardAction(package, action, &rc);
7304 if (!handled)
7305 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7307 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7308 handled = TRUE;
7310 if (!handled)
7312 WARN("unhandled msi action %s\n", debugstr_w(action));
7313 rc = ERROR_FUNCTION_NOT_CALLED;
7316 return rc;
7319 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7321 UINT rc = ERROR_SUCCESS;
7322 MSIRECORD *row;
7324 static const WCHAR query[] =
7325 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7326 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7327 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7328 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7329 static const WCHAR ui_query[] =
7330 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7331 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7332 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7333 ' ', '=',' ','%','i',0};
7335 if (needs_ui_sequence(package))
7336 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7337 else
7338 row = MSI_QueryGetRecord(package->db, query, seq);
7340 if (row)
7342 LPCWSTR action, cond;
7344 TRACE("Running the actions\n");
7346 /* check conditions */
7347 cond = MSI_RecordGetString(row, 2);
7349 /* this is a hack to skip errors in the condition code */
7350 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7352 msiobj_release(&row->hdr);
7353 return ERROR_SUCCESS;
7356 action = MSI_RecordGetString(row, 1);
7357 if (!action)
7359 ERR("failed to fetch action\n");
7360 msiobj_release(&row->hdr);
7361 return ERROR_FUNCTION_FAILED;
7364 if (needs_ui_sequence(package))
7365 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7366 else
7367 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7369 msiobj_release(&row->hdr);
7372 return rc;
7375 /****************************************************
7376 * TOP level entry points
7377 *****************************************************/
7379 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7380 LPCWSTR szCommandLine )
7382 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7383 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7384 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7385 WCHAR *reinstall = NULL;
7386 BOOL ui_exists;
7387 UINT rc;
7389 msi_set_property( package->db, szAction, szInstall );
7391 package->script->InWhatSequence = SEQUENCE_INSTALL;
7393 if (szPackagePath)
7395 LPWSTR p, dir;
7396 LPCWSTR file;
7398 dir = strdupW(szPackagePath);
7399 p = strrchrW(dir, '\\');
7400 if (p)
7402 *(++p) = 0;
7403 file = szPackagePath + (p - dir);
7405 else
7407 msi_free(dir);
7408 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7409 GetCurrentDirectoryW(MAX_PATH, dir);
7410 lstrcatW(dir, szBackSlash);
7411 file = szPackagePath;
7414 msi_free( package->PackagePath );
7415 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7416 if (!package->PackagePath)
7418 msi_free(dir);
7419 return ERROR_OUTOFMEMORY;
7422 lstrcpyW(package->PackagePath, dir);
7423 lstrcatW(package->PackagePath, file);
7424 msi_free(dir);
7426 msi_set_sourcedir_props(package, FALSE);
7429 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7430 if (rc != ERROR_SUCCESS)
7431 return rc;
7433 msi_apply_transforms( package );
7434 msi_apply_patches( package );
7436 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7438 TRACE("setting reinstall property\n");
7439 msi_set_property( package->db, szReinstall, szAll );
7442 /* properties may have been added by a transform */
7443 msi_clone_properties( package );
7445 msi_parse_command_line( package, szCommandLine, FALSE );
7446 msi_adjust_privilege_properties( package );
7447 msi_set_context( package );
7449 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7451 TRACE("disabling rollback\n");
7452 msi_set_property( package->db, szRollbackDisabled, szOne );
7455 if (needs_ui_sequence( package))
7457 package->script->InWhatSequence |= SEQUENCE_UI;
7458 rc = ACTION_ProcessUISequence(package);
7459 ui_exists = ui_sequence_exists(package);
7460 if (rc == ERROR_SUCCESS || !ui_exists)
7462 package->script->InWhatSequence |= SEQUENCE_EXEC;
7463 rc = ACTION_ProcessExecSequence(package, ui_exists);
7466 else
7467 rc = ACTION_ProcessExecSequence(package, FALSE);
7469 package->script->CurrentlyScripting = FALSE;
7471 /* process the ending type action */
7472 if (rc == ERROR_SUCCESS)
7473 ACTION_PerformActionSequence(package, -1);
7474 else if (rc == ERROR_INSTALL_USEREXIT)
7475 ACTION_PerformActionSequence(package, -2);
7476 else if (rc == ERROR_INSTALL_SUSPEND)
7477 ACTION_PerformActionSequence(package, -4);
7478 else /* failed */
7480 ACTION_PerformActionSequence(package, -3);
7481 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7483 package->need_rollback = TRUE;
7487 /* finish up running custom actions */
7488 ACTION_FinishCustomActions(package);
7490 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7492 WARN("installation failed, running rollback script\n");
7493 execute_script( package, SCRIPT_ROLLBACK );
7495 msi_free( reinstall );
7497 if (rc == ERROR_SUCCESS && package->need_reboot)
7498 return ERROR_SUCCESS_REBOOT_REQUIRED;
7500 return rc;