usp10: Properly set LayoutRTL.
[wine.git] / dlls / msi / action.c
blob6bc4fff2cd9b389c6dd500232c6d4fc277506eda
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, -1);
528 else
529 rc = ACTION_PerformAction(package, action, -1);
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 == ROLLBACK_SCRIPT)
1585 for (i = package->script->ActionCount[script]; i > 0; i--)
1587 rc = execute_script_action( package, script, i - 1 );
1588 if (rc != ERROR_SUCCESS) break;
1591 else
1593 for (i = 0; i < package->script->ActionCount[script]; i++)
1595 rc = execute_script_action( package, script, i );
1596 if (rc != ERROR_SUCCESS) break;
1599 msi_free_action_script(package, script);
1600 return rc;
1603 static UINT ACTION_FileCost(MSIPACKAGE *package)
1605 return ERROR_SUCCESS;
1608 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1610 MSICOMPONENT *comp;
1611 UINT r;
1613 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1615 if (!comp->ComponentId) continue;
1617 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1618 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1619 &comp->Installed );
1620 if (r != ERROR_SUCCESS)
1621 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1622 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1623 &comp->Installed );
1624 if (r != ERROR_SUCCESS)
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1627 &comp->Installed );
1628 if (r != ERROR_SUCCESS)
1629 comp->Installed = INSTALLSTATE_ABSENT;
1633 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1635 MSIFEATURE *feature;
1637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1639 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1641 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1642 feature->Installed = INSTALLSTATE_ABSENT;
1643 else
1644 feature->Installed = state;
1648 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1650 return (feature->Level > 0 && feature->Level <= level);
1653 static BOOL process_state_property(MSIPACKAGE* package, int level,
1654 LPCWSTR property, INSTALLSTATE state)
1656 LPWSTR override;
1657 MSIFEATURE *feature;
1659 override = msi_dup_property( package->db, property );
1660 if (!override)
1661 return FALSE;
1663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1665 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1666 continue;
1668 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1670 if (!strcmpiW( override, szAll ))
1672 if (feature->Installed != state)
1674 feature->Action = state;
1675 feature->ActionRequest = state;
1678 else
1680 LPWSTR ptr = override;
1681 LPWSTR ptr2 = strchrW(override,',');
1683 while (ptr)
1685 int len = ptr2 - ptr;
1687 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1688 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1690 if (feature->Installed != state)
1692 feature->Action = state;
1693 feature->ActionRequest = state;
1695 break;
1697 if (ptr2)
1699 ptr=ptr2+1;
1700 ptr2 = strchrW(ptr,',');
1702 else
1703 break;
1707 msi_free(override);
1708 return TRUE;
1711 static BOOL process_overrides( MSIPACKAGE *package, int level )
1713 static const WCHAR szAddLocal[] =
1714 {'A','D','D','L','O','C','A','L',0};
1715 static const WCHAR szAddSource[] =
1716 {'A','D','D','S','O','U','R','C','E',0};
1717 static const WCHAR szAdvertise[] =
1718 {'A','D','V','E','R','T','I','S','E',0};
1719 BOOL ret = FALSE;
1721 /* all these activation/deactivation things happen in order and things
1722 * later on the list override things earlier on the list.
1724 * 0 INSTALLLEVEL processing
1725 * 1 ADDLOCAL
1726 * 2 REMOVE
1727 * 3 ADDSOURCE
1728 * 4 ADDDEFAULT
1729 * 5 REINSTALL
1730 * 6 ADVERTISE
1731 * 7 COMPADDLOCAL
1732 * 8 COMPADDSOURCE
1733 * 9 FILEADDLOCAL
1734 * 10 FILEADDSOURCE
1735 * 11 FILEADDDEFAULT
1737 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1738 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1739 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1740 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1741 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1743 if (ret)
1744 msi_set_property( package->db, szPreselected, szOne );
1746 return ret;
1749 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1751 int level;
1752 MSICOMPONENT* component;
1753 MSIFEATURE *feature;
1755 TRACE("Checking Install Level\n");
1757 level = msi_get_property_int(package->db, szInstallLevel, 1);
1759 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1761 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1763 if (!is_feature_selected( feature, level )) continue;
1765 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1767 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1769 feature->Action = INSTALLSTATE_SOURCE;
1770 feature->ActionRequest = INSTALLSTATE_SOURCE;
1772 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1774 feature->Action = INSTALLSTATE_ADVERTISED;
1775 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1777 else
1779 feature->Action = INSTALLSTATE_LOCAL;
1780 feature->ActionRequest = INSTALLSTATE_LOCAL;
1784 /* disable child features of unselected parent or follow parent */
1785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1787 FeatureList *fl;
1789 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1791 if (!is_feature_selected( feature, level ))
1793 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1794 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1796 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1798 fl->feature->Action = feature->Action;
1799 fl->feature->ActionRequest = feature->ActionRequest;
1804 else /* preselected */
1806 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1808 if (!is_feature_selected( feature, level )) continue;
1810 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1812 if (feature->Installed == INSTALLSTATE_ABSENT)
1814 feature->Action = INSTALLSTATE_UNKNOWN;
1815 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1817 else
1819 feature->Action = feature->Installed;
1820 feature->ActionRequest = feature->Installed;
1824 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1826 FeatureList *fl;
1828 if (!is_feature_selected( feature, level )) continue;
1830 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1832 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1834 fl->feature->Action = feature->Action;
1835 fl->feature->ActionRequest = feature->ActionRequest;
1841 /* now we want to set component state based based on feature state */
1842 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 ComponentList *cl;
1846 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1847 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1848 feature->ActionRequest, feature->Action);
1850 if (!is_feature_selected( feature, level )) continue;
1852 /* features with components that have compressed files are made local */
1853 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1855 if (cl->component->ForceLocalState &&
1856 feature->ActionRequest == INSTALLSTATE_SOURCE)
1858 feature->Action = INSTALLSTATE_LOCAL;
1859 feature->ActionRequest = INSTALLSTATE_LOCAL;
1860 break;
1864 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1866 component = cl->component;
1868 switch (feature->ActionRequest)
1870 case INSTALLSTATE_ABSENT:
1871 component->anyAbsent = 1;
1872 break;
1873 case INSTALLSTATE_ADVERTISED:
1874 component->hasAdvertiseFeature = 1;
1875 break;
1876 case INSTALLSTATE_SOURCE:
1877 component->hasSourceFeature = 1;
1878 break;
1879 case INSTALLSTATE_LOCAL:
1880 component->hasLocalFeature = 1;
1881 break;
1882 case INSTALLSTATE_DEFAULT:
1883 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1884 component->hasAdvertiseFeature = 1;
1885 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1886 component->hasSourceFeature = 1;
1887 else
1888 component->hasLocalFeature = 1;
1889 break;
1890 default:
1891 break;
1896 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1898 /* check if it's local or source */
1899 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1900 (component->hasLocalFeature || component->hasSourceFeature))
1902 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1903 !component->ForceLocalState)
1905 component->Action = INSTALLSTATE_SOURCE;
1906 component->ActionRequest = INSTALLSTATE_SOURCE;
1908 else
1910 component->Action = INSTALLSTATE_LOCAL;
1911 component->ActionRequest = INSTALLSTATE_LOCAL;
1913 continue;
1916 /* if any feature is local, the component must be local too */
1917 if (component->hasLocalFeature)
1919 component->Action = INSTALLSTATE_LOCAL;
1920 component->ActionRequest = INSTALLSTATE_LOCAL;
1921 continue;
1923 if (component->hasSourceFeature)
1925 component->Action = INSTALLSTATE_SOURCE;
1926 component->ActionRequest = INSTALLSTATE_SOURCE;
1927 continue;
1929 if (component->hasAdvertiseFeature)
1931 component->Action = INSTALLSTATE_ADVERTISED;
1932 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1933 continue;
1935 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1936 if (component->anyAbsent &&
1937 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1939 component->Action = INSTALLSTATE_ABSENT;
1940 component->ActionRequest = INSTALLSTATE_ABSENT;
1944 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1946 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1948 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1949 component->Action = INSTALLSTATE_LOCAL;
1950 component->ActionRequest = INSTALLSTATE_LOCAL;
1953 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1954 component->Installed == INSTALLSTATE_SOURCE &&
1955 component->hasSourceFeature)
1957 component->Action = INSTALLSTATE_UNKNOWN;
1958 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1961 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1962 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1965 return ERROR_SUCCESS;
1968 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1970 MSIPACKAGE *package = param;
1971 LPCWSTR name;
1972 MSIFEATURE *feature;
1974 name = MSI_RecordGetString( row, 1 );
1976 feature = msi_get_loaded_feature( package, name );
1977 if (!feature)
1978 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1979 else
1981 LPCWSTR Condition;
1982 Condition = MSI_RecordGetString(row,3);
1984 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1986 int level = MSI_RecordGetInteger(row,2);
1987 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1988 feature->Level = level;
1991 return ERROR_SUCCESS;
1994 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1996 static const WCHAR name[] = {'\\',0};
1997 VS_FIXEDFILEINFO *ptr, *ret;
1998 LPVOID version;
1999 DWORD versize, handle;
2000 UINT sz;
2002 versize = GetFileVersionInfoSizeW( filename, &handle );
2003 if (!versize)
2004 return NULL;
2006 version = msi_alloc( versize );
2007 if (!version)
2008 return NULL;
2010 GetFileVersionInfoW( filename, 0, versize, version );
2012 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2014 msi_free( version );
2015 return NULL;
2018 ret = msi_alloc( sz );
2019 memcpy( ret, ptr, sz );
2021 msi_free( version );
2022 return ret;
2025 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2027 DWORD ms, ls;
2029 msi_parse_version_string( version, &ms, &ls );
2031 if (fi->dwFileVersionMS > ms) return 1;
2032 else if (fi->dwFileVersionMS < ms) return -1;
2033 else if (fi->dwFileVersionLS > ls) return 1;
2034 else if (fi->dwFileVersionLS < ls) return -1;
2035 return 0;
2038 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2040 DWORD ms1, ms2;
2042 msi_parse_version_string( ver1, &ms1, NULL );
2043 msi_parse_version_string( ver2, &ms2, NULL );
2045 if (ms1 > ms2) return 1;
2046 else if (ms1 < ms2) return -1;
2047 return 0;
2050 DWORD msi_get_disk_file_size( LPCWSTR filename )
2052 HANDLE file;
2053 DWORD size;
2055 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2056 if (file == INVALID_HANDLE_VALUE)
2057 return INVALID_FILE_SIZE;
2059 size = GetFileSize( file, NULL );
2060 TRACE("size is %u\n", size);
2061 CloseHandle( file );
2062 return size;
2065 BOOL msi_file_hash_matches( MSIFILE *file )
2067 UINT r;
2068 MSIFILEHASHINFO hash;
2070 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2071 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2072 if (r != ERROR_SUCCESS)
2073 return FALSE;
2075 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2078 static WCHAR *get_temp_dir( void )
2080 static UINT id;
2081 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2083 GetTempPathW( MAX_PATH, tmp );
2084 for (;;)
2086 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2087 if (CreateDirectoryW( dir, NULL )) break;
2089 return strdupW( dir );
2093 * msi_build_directory_name()
2095 * This function is to save messing round with directory names
2096 * It handles adding backslashes between path segments,
2097 * and can add \ at the end of the directory name if told to.
2099 * It takes a variable number of arguments.
2100 * It always allocates a new string for the result, so make sure
2101 * to free the return value when finished with it.
2103 * The first arg is the number of path segments that follow.
2104 * The arguments following count are a list of path segments.
2105 * A path segment may be NULL.
2107 * Path segments will be added with a \ separating them.
2108 * A \ will not be added after the last segment, however if the
2109 * last segment is NULL, then the last character will be a \
2111 WCHAR *msi_build_directory_name( DWORD count, ... )
2113 DWORD sz = 1, i;
2114 WCHAR *dir;
2115 va_list va;
2117 va_start( va, count );
2118 for (i = 0; i < count; i++)
2120 const WCHAR *str = va_arg( va, const WCHAR * );
2121 if (str) sz += strlenW( str ) + 1;
2123 va_end( va );
2125 dir = msi_alloc( sz * sizeof(WCHAR) );
2126 dir[0] = 0;
2128 va_start( va, count );
2129 for (i = 0; i < count; i++)
2131 const WCHAR *str = va_arg( va, const WCHAR * );
2132 if (!str) continue;
2133 strcatW( dir, str );
2134 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2136 va_end( va );
2137 return dir;
2140 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2142 MSIASSEMBLY *assembly = file->Component->assembly;
2144 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2146 msi_free( file->TargetPath );
2147 if (assembly && !assembly->application)
2149 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2150 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2151 msi_track_tempfile( package, file->TargetPath );
2153 else
2155 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2156 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2159 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2162 static UINT calculate_file_cost( MSIPACKAGE *package )
2164 VS_FIXEDFILEINFO *file_version;
2165 WCHAR *font_version;
2166 MSIFILE *file;
2168 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2170 MSICOMPONENT *comp = file->Component;
2171 DWORD file_size;
2173 if (!comp->Enabled) continue;
2175 if (file->IsCompressed)
2176 comp->ForceLocalState = TRUE;
2178 set_target_path( package, file );
2180 if ((comp->assembly && !comp->assembly->installed) ||
2181 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2183 comp->Cost += file->FileSize;
2184 continue;
2186 file_size = msi_get_disk_file_size( file->TargetPath );
2188 if (file->Version)
2190 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2192 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2194 comp->Cost += file->FileSize - file_size;
2196 msi_free( file_version );
2197 continue;
2199 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2201 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2203 comp->Cost += file->FileSize - file_size;
2205 msi_free( font_version );
2206 continue;
2209 if (file_size != file->FileSize)
2211 comp->Cost += file->FileSize - file_size;
2214 return ERROR_SUCCESS;
2217 void msi_clean_path( WCHAR *p )
2219 WCHAR *q = p;
2220 int n, len = 0;
2222 while (1)
2224 /* copy until the end of the string or a space */
2225 while (*p != ' ' && (*q = *p))
2227 p++, len++;
2228 /* reduce many backslashes to one */
2229 if (*p != '\\' || *q != '\\')
2230 q++;
2233 /* quit at the end of the string */
2234 if (!*p)
2235 break;
2237 /* count the number of spaces */
2238 n = 0;
2239 while (p[n] == ' ')
2240 n++;
2242 /* if it's leading or trailing space, skip it */
2243 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2244 p += n;
2245 else /* copy n spaces */
2246 while (n && (*q++ = *p++)) n--;
2250 static WCHAR *get_target_dir_property( MSIDATABASE *db )
2252 int len;
2253 WCHAR *path, *target_dir = msi_dup_property( db, szTargetDir );
2255 if (!target_dir) return NULL;
2257 len = strlenW( target_dir );
2258 if (target_dir[len - 1] == '\\') return target_dir;
2259 if ((path = msi_alloc( (len + 2) * sizeof(WCHAR) )))
2261 strcpyW( path, target_dir );
2262 path[len] = '\\';
2263 path[len + 1] = 0;
2265 msi_free( target_dir );
2266 return path;
2269 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2271 FolderList *fl;
2272 MSIFOLDER *folder, *parent, *child;
2273 WCHAR *path;
2275 TRACE("resolving %s\n", debugstr_w(name));
2277 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2279 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2281 if (!load_prop || !(path = get_target_dir_property( package->db )))
2283 path = msi_dup_property( package->db, szRootDrive );
2286 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2288 parent = msi_get_loaded_folder( package, folder->Parent );
2289 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2291 msi_clean_path( path );
2292 if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2294 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2295 msi_free( path );
2296 return;
2298 msi_set_property( package->db, folder->Directory, path );
2299 msi_free( folder->ResolvedTarget );
2300 folder->ResolvedTarget = path;
2302 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2304 child = fl->folder;
2305 msi_resolve_target_folder( package, child->Directory, load_prop );
2307 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2310 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2312 static const WCHAR query[] = {
2313 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2314 '`','C','o','n','d','i','t','i','o','n','`',0};
2315 static const WCHAR szOutOfDiskSpace[] = {
2316 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2317 MSICOMPONENT *comp;
2318 MSIQUERY *view;
2319 LPWSTR level;
2320 UINT rc;
2322 TRACE("Building directory properties\n");
2323 msi_resolve_target_folder( package, szTargetDir, TRUE );
2325 TRACE("Evaluating component conditions\n");
2326 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2328 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2330 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2331 comp->Enabled = FALSE;
2333 else
2334 comp->Enabled = TRUE;
2337 /* read components states from the registry */
2338 ACTION_GetComponentInstallStates(package);
2339 ACTION_GetFeatureInstallStates(package);
2341 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2343 TRACE("Evaluating feature conditions\n");
2345 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2346 if (rc == ERROR_SUCCESS)
2348 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2349 msiobj_release( &view->hdr );
2350 if (rc != ERROR_SUCCESS)
2351 return rc;
2355 TRACE("Calculating file cost\n");
2356 calculate_file_cost( package );
2358 msi_set_property( package->db, szCostingComplete, szOne );
2359 /* set default run level if not set */
2360 level = msi_dup_property( package->db, szInstallLevel );
2361 if (!level)
2362 msi_set_property( package->db, szInstallLevel, szOne );
2363 msi_free(level);
2365 /* FIXME: check volume disk space */
2366 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2368 return MSI_SetFeatureStates(package);
2371 /* OK this value is "interpreted" and then formatted based on the
2372 first few characters */
2373 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2374 DWORD *size)
2376 LPSTR data = NULL;
2378 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2380 if (value[1]=='x')
2382 LPWSTR ptr;
2383 CHAR byte[5];
2384 LPWSTR deformated = NULL;
2385 int count;
2387 deformat_string(package, &value[2], &deformated);
2389 /* binary value type */
2390 ptr = deformated;
2391 *type = REG_BINARY;
2392 if (strlenW(ptr)%2)
2393 *size = (strlenW(ptr)/2)+1;
2394 else
2395 *size = strlenW(ptr)/2;
2397 data = msi_alloc(*size);
2399 byte[0] = '0';
2400 byte[1] = 'x';
2401 byte[4] = 0;
2402 count = 0;
2403 /* if uneven pad with a zero in front */
2404 if (strlenW(ptr)%2)
2406 byte[2]= '0';
2407 byte[3]= *ptr;
2408 ptr++;
2409 data[count] = (BYTE)strtol(byte,NULL,0);
2410 count ++;
2411 TRACE("Uneven byte count\n");
2413 while (*ptr)
2415 byte[2]= *ptr;
2416 ptr++;
2417 byte[3]= *ptr;
2418 ptr++;
2419 data[count] = (BYTE)strtol(byte,NULL,0);
2420 count ++;
2422 msi_free(deformated);
2424 TRACE("Data %i bytes(%i)\n",*size,count);
2426 else
2428 LPWSTR deformated;
2429 LPWSTR p;
2430 DWORD d = 0;
2431 deformat_string(package, &value[1], &deformated);
2433 *type=REG_DWORD;
2434 *size = sizeof(DWORD);
2435 data = msi_alloc(*size);
2436 p = deformated;
2437 if (*p == '-')
2438 p++;
2439 while (*p)
2441 if ( (*p < '0') || (*p > '9') )
2442 break;
2443 d *= 10;
2444 d += (*p - '0');
2445 p++;
2447 if (deformated[0] == '-')
2448 d = -d;
2449 *(LPDWORD)data = d;
2450 TRACE("DWORD %i\n",*(LPDWORD)data);
2452 msi_free(deformated);
2455 else
2457 static const WCHAR szMulti[] = {'[','~',']',0};
2458 LPCWSTR ptr;
2459 *type=REG_SZ;
2461 if (value[0]=='#')
2463 if (value[1]=='%')
2465 ptr = &value[2];
2466 *type=REG_EXPAND_SZ;
2468 else
2469 ptr = &value[1];
2471 else
2472 ptr=value;
2474 if (strstrW(value, szMulti))
2475 *type = REG_MULTI_SZ;
2477 /* remove initial delimiter */
2478 if (!strncmpW(value, szMulti, 3))
2479 ptr = value + 3;
2481 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2483 /* add double NULL terminator */
2484 if (*type == REG_MULTI_SZ)
2486 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2487 data = msi_realloc_zero(data, *size);
2490 return data;
2493 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2495 const WCHAR *ret;
2497 switch (root)
2499 case -1:
2500 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2502 *root_key = HKEY_LOCAL_MACHINE;
2503 ret = szHLM;
2505 else
2507 *root_key = HKEY_CURRENT_USER;
2508 ret = szHCU;
2510 break;
2511 case 0:
2512 *root_key = HKEY_CLASSES_ROOT;
2513 ret = szHCR;
2514 break;
2515 case 1:
2516 *root_key = HKEY_CURRENT_USER;
2517 ret = szHCU;
2518 break;
2519 case 2:
2520 *root_key = HKEY_LOCAL_MACHINE;
2521 ret = szHLM;
2522 break;
2523 case 3:
2524 *root_key = HKEY_USERS;
2525 ret = szHU;
2526 break;
2527 default:
2528 ERR("Unknown root %i\n", root);
2529 return NULL;
2532 return ret;
2535 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2537 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2538 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2540 if (is_64bit && package->platform == PLATFORM_INTEL &&
2541 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2543 UINT size;
2544 WCHAR *path_32node;
2546 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2547 if (!(path_32node = msi_alloc( size ))) return NULL;
2549 memcpy( path_32node, path, len * sizeof(WCHAR) );
2550 strcpyW( path_32node + len, szWow6432Node );
2551 strcatW( path_32node, szBackSlash );
2552 strcatW( path_32node, path + len );
2553 return path_32node;
2556 return strdupW( path );
2559 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2561 MSIPACKAGE *package = param;
2562 LPSTR value_data = NULL;
2563 HKEY root_key, hkey;
2564 DWORD type,size;
2565 LPWSTR deformated, uikey, keypath;
2566 LPCWSTR szRoot, component, name, key, value;
2567 MSICOMPONENT *comp;
2568 MSIRECORD * uirow;
2569 INT root;
2570 BOOL check_first = FALSE;
2571 UINT rc;
2573 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2575 component = MSI_RecordGetString(row, 6);
2576 comp = msi_get_loaded_component(package,component);
2577 if (!comp)
2578 return ERROR_SUCCESS;
2580 comp->Action = msi_get_component_action( package, comp );
2581 if (comp->Action != INSTALLSTATE_LOCAL)
2583 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2584 return ERROR_SUCCESS;
2587 name = MSI_RecordGetString(row, 4);
2588 if( MSI_RecordIsNull(row,5) && name )
2590 /* null values can have special meanings */
2591 if (name[0]=='-' && name[1] == 0)
2592 return ERROR_SUCCESS;
2593 else if ((name[0]=='+' && name[1] == 0) ||
2594 (name[0] == '*' && name[1] == 0))
2595 name = NULL;
2596 check_first = TRUE;
2599 root = MSI_RecordGetInteger(row,2);
2600 key = MSI_RecordGetString(row, 3);
2602 szRoot = get_root_key( package, root, &root_key );
2603 if (!szRoot)
2604 return ERROR_SUCCESS;
2606 deformat_string(package, key , &deformated);
2607 size = strlenW(deformated) + strlenW(szRoot) + 1;
2608 uikey = msi_alloc(size*sizeof(WCHAR));
2609 strcpyW(uikey,szRoot);
2610 strcatW(uikey,deformated);
2612 keypath = get_keypath( package, root_key, deformated );
2613 msi_free( deformated );
2614 if (RegCreateKeyW( root_key, keypath, &hkey ))
2616 ERR("Could not create key %s\n", debugstr_w(keypath));
2617 msi_free(uikey);
2618 msi_free(keypath);
2619 return ERROR_SUCCESS;
2622 value = MSI_RecordGetString(row,5);
2623 if (value)
2624 value_data = parse_value(package, value, &type, &size);
2625 else
2627 value_data = (LPSTR)strdupW(szEmpty);
2628 size = sizeof(szEmpty);
2629 type = REG_SZ;
2632 deformat_string(package, name, &deformated);
2634 if (!check_first)
2636 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2637 debugstr_w(uikey));
2638 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2640 else
2642 DWORD sz = 0;
2643 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2644 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2646 TRACE("value %s of %s checked already exists\n",
2647 debugstr_w(deformated), debugstr_w(uikey));
2649 else
2651 TRACE("Checked and setting value %s of %s\n",
2652 debugstr_w(deformated), debugstr_w(uikey));
2653 if (deformated || size)
2654 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2657 RegCloseKey(hkey);
2659 uirow = MSI_CreateRecord(3);
2660 MSI_RecordSetStringW(uirow,2,deformated);
2661 MSI_RecordSetStringW(uirow,1,uikey);
2662 if (type == REG_SZ || type == REG_EXPAND_SZ)
2663 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2664 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2665 msiobj_release( &uirow->hdr );
2667 msi_free(value_data);
2668 msi_free(deformated);
2669 msi_free(uikey);
2670 msi_free(keypath);
2672 return ERROR_SUCCESS;
2675 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2677 static const WCHAR query[] = {
2678 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2679 '`','R','e','g','i','s','t','r','y','`',0};
2680 MSIQUERY *view;
2681 UINT rc;
2683 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2684 if (rc != ERROR_SUCCESS)
2685 return ERROR_SUCCESS;
2687 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2688 msiobj_release(&view->hdr);
2689 return rc;
2692 static void delete_reg_key_or_value( HKEY hkey_root, LPCWSTR key, LPCWSTR value, BOOL delete_key )
2694 LONG res;
2695 HKEY hkey;
2696 DWORD num_subkeys, num_values;
2698 if (delete_key)
2700 if ((res = RegDeleteTreeW( hkey_root, key )))
2702 TRACE("Failed to delete key %s (%d)\n", debugstr_w(key), res);
2704 return;
2707 if (!(res = RegOpenKeyW( hkey_root, key, &hkey )))
2709 if ((res = RegDeleteValueW( hkey, value )))
2711 TRACE("Failed to delete value %s (%d)\n", debugstr_w(value), res);
2713 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2714 NULL, NULL, NULL, NULL );
2715 RegCloseKey( hkey );
2716 if (!res && !num_subkeys && !num_values)
2718 TRACE("Removing empty key %s\n", debugstr_w(key));
2719 RegDeleteKeyW( hkey_root, key );
2721 return;
2723 TRACE("Failed to open key %s (%d)\n", debugstr_w(key), res);
2727 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2729 MSIPACKAGE *package = param;
2730 LPCWSTR component, name, key_str, root_key_str;
2731 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2732 MSICOMPONENT *comp;
2733 MSIRECORD *uirow;
2734 BOOL delete_key = FALSE;
2735 HKEY hkey_root;
2736 UINT size;
2737 INT root;
2739 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2741 component = MSI_RecordGetString( row, 6 );
2742 comp = msi_get_loaded_component( package, component );
2743 if (!comp)
2744 return ERROR_SUCCESS;
2746 comp->Action = msi_get_component_action( package, comp );
2747 if (comp->Action != INSTALLSTATE_ABSENT)
2749 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2750 return ERROR_SUCCESS;
2753 name = MSI_RecordGetString( row, 4 );
2754 if (MSI_RecordIsNull( row, 5 ) && name )
2756 if (name[0] == '+' && !name[1])
2757 return ERROR_SUCCESS;
2758 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2760 delete_key = TRUE;
2761 name = NULL;
2765 root = MSI_RecordGetInteger( row, 2 );
2766 key_str = MSI_RecordGetString( row, 3 );
2768 root_key_str = get_root_key( package, root, &hkey_root );
2769 if (!root_key_str)
2770 return ERROR_SUCCESS;
2772 deformat_string( package, key_str, &deformated_key );
2773 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2774 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2775 strcpyW( ui_key_str, root_key_str );
2776 strcatW( ui_key_str, deformated_key );
2778 deformat_string( package, name, &deformated_name );
2780 keypath = get_keypath( package, hkey_root, deformated_key );
2781 msi_free( deformated_key );
2782 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2783 msi_free( keypath );
2785 uirow = MSI_CreateRecord( 2 );
2786 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2787 MSI_RecordSetStringW( uirow, 2, deformated_name );
2788 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2789 msiobj_release( &uirow->hdr );
2791 msi_free( ui_key_str );
2792 msi_free( deformated_name );
2793 return ERROR_SUCCESS;
2796 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2798 MSIPACKAGE *package = param;
2799 LPCWSTR component, name, key_str, root_key_str;
2800 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2801 MSICOMPONENT *comp;
2802 MSIRECORD *uirow;
2803 BOOL delete_key = FALSE;
2804 HKEY hkey_root;
2805 UINT size;
2806 INT root;
2808 component = MSI_RecordGetString( row, 5 );
2809 comp = msi_get_loaded_component( package, component );
2810 if (!comp)
2811 return ERROR_SUCCESS;
2813 comp->Action = msi_get_component_action( package, comp );
2814 if (comp->Action != INSTALLSTATE_LOCAL)
2816 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2817 return ERROR_SUCCESS;
2820 if ((name = MSI_RecordGetString( row, 4 )))
2822 if (name[0] == '-' && !name[1])
2824 delete_key = TRUE;
2825 name = NULL;
2829 root = MSI_RecordGetInteger( row, 2 );
2830 key_str = MSI_RecordGetString( row, 3 );
2832 root_key_str = get_root_key( package, root, &hkey_root );
2833 if (!root_key_str)
2834 return ERROR_SUCCESS;
2836 deformat_string( package, key_str, &deformated_key );
2837 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2838 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2839 strcpyW( ui_key_str, root_key_str );
2840 strcatW( ui_key_str, deformated_key );
2842 deformat_string( package, name, &deformated_name );
2844 keypath = get_keypath( package, hkey_root, deformated_key );
2845 msi_free( deformated_key );
2846 delete_reg_key_or_value( hkey_root, keypath, deformated_name, delete_key );
2847 msi_free( keypath );
2849 uirow = MSI_CreateRecord( 2 );
2850 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2851 MSI_RecordSetStringW( uirow, 2, deformated_name );
2852 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2853 msiobj_release( &uirow->hdr );
2855 msi_free( ui_key_str );
2856 msi_free( deformated_name );
2857 return ERROR_SUCCESS;
2860 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2862 static const WCHAR registry_query[] = {
2863 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2864 '`','R','e','g','i','s','t','r','y','`',0};
2865 static const WCHAR remove_registry_query[] = {
2866 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2867 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2868 MSIQUERY *view;
2869 UINT rc;
2871 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2872 if (rc == ERROR_SUCCESS)
2874 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2875 msiobj_release( &view->hdr );
2876 if (rc != ERROR_SUCCESS)
2877 return rc;
2879 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2880 if (rc == ERROR_SUCCESS)
2882 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2883 msiobj_release( &view->hdr );
2884 if (rc != ERROR_SUCCESS)
2885 return rc;
2887 return ERROR_SUCCESS;
2890 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2892 package->script->CurrentlyScripting = TRUE;
2894 return ERROR_SUCCESS;
2898 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2900 static const WCHAR query[]= {
2901 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2902 '`','R','e','g','i','s','t','r','y','`',0};
2903 MSICOMPONENT *comp;
2904 DWORD total = 0, count = 0;
2905 MSIQUERY *view;
2906 MSIFEATURE *feature;
2907 MSIFILE *file;
2908 UINT rc;
2910 TRACE("InstallValidate\n");
2912 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2913 if (rc == ERROR_SUCCESS)
2915 rc = MSI_IterateRecords( view, &count, NULL, package );
2916 msiobj_release( &view->hdr );
2917 if (rc != ERROR_SUCCESS)
2918 return rc;
2919 total += count * REG_PROGRESS_VALUE;
2921 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2922 total += COMPONENT_PROGRESS_VALUE;
2924 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2925 total += file->FileSize;
2927 msi_ui_progress( package, 0, total, 0, 0 );
2929 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2931 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2932 debugstr_w(feature->Feature), feature->Installed,
2933 feature->ActionRequest, feature->Action);
2935 return ERROR_SUCCESS;
2938 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2940 MSIPACKAGE* package = param;
2941 LPCWSTR cond = NULL;
2942 LPCWSTR message = NULL;
2943 UINT r;
2945 static const WCHAR title[]=
2946 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2948 cond = MSI_RecordGetString(row,1);
2950 r = MSI_EvaluateConditionW(package,cond);
2951 if (r == MSICONDITION_FALSE)
2953 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2955 LPWSTR deformated;
2956 message = MSI_RecordGetString(row,2);
2957 deformat_string(package,message,&deformated);
2958 MessageBoxW(NULL,deformated,title,MB_OK);
2959 msi_free(deformated);
2962 return ERROR_INSTALL_FAILURE;
2965 return ERROR_SUCCESS;
2968 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2970 static const WCHAR query[] = {
2971 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2972 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2973 MSIQUERY *view;
2974 UINT rc;
2976 TRACE("Checking launch conditions\n");
2978 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2979 if (rc != ERROR_SUCCESS)
2980 return ERROR_SUCCESS;
2982 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2983 msiobj_release(&view->hdr);
2984 return rc;
2987 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2990 if (!cmp->KeyPath)
2991 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2993 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2995 static const WCHAR query[] = {
2996 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2997 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2998 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2999 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3000 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3001 MSIRECORD *row;
3002 UINT root, len;
3003 LPWSTR deformated, buffer, deformated_name;
3004 LPCWSTR key, name;
3006 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3007 if (!row)
3008 return NULL;
3010 root = MSI_RecordGetInteger(row,2);
3011 key = MSI_RecordGetString(row, 3);
3012 name = MSI_RecordGetString(row, 4);
3013 deformat_string(package, key , &deformated);
3014 deformat_string(package, name, &deformated_name);
3016 len = strlenW(deformated) + 6;
3017 if (deformated_name)
3018 len+=strlenW(deformated_name);
3020 buffer = msi_alloc( len *sizeof(WCHAR));
3022 if (deformated_name)
3023 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3024 else
3025 sprintfW(buffer,fmt,root,deformated);
3027 msi_free(deformated);
3028 msi_free(deformated_name);
3029 msiobj_release(&row->hdr);
3031 return buffer;
3033 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3035 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3036 return NULL;
3038 else
3040 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3042 if (file)
3043 return strdupW( file->TargetPath );
3045 return NULL;
3048 static HKEY openSharedDLLsKey(void)
3050 HKEY hkey=0;
3051 static const WCHAR path[] =
3052 {'S','o','f','t','w','a','r','e','\\',
3053 'M','i','c','r','o','s','o','f','t','\\',
3054 'W','i','n','d','o','w','s','\\',
3055 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3056 'S','h','a','r','e','d','D','L','L','s',0};
3058 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3059 return hkey;
3062 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3064 HKEY hkey;
3065 DWORD count=0;
3066 DWORD type;
3067 DWORD sz = sizeof(count);
3068 DWORD rc;
3070 hkey = openSharedDLLsKey();
3071 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3072 if (rc != ERROR_SUCCESS)
3073 count = 0;
3074 RegCloseKey(hkey);
3075 return count;
3078 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3080 HKEY hkey;
3082 hkey = openSharedDLLsKey();
3083 if (count > 0)
3084 msi_reg_set_val_dword( hkey, path, count );
3085 else
3086 RegDeleteValueW(hkey,path);
3087 RegCloseKey(hkey);
3088 return count;
3091 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3093 MSIFEATURE *feature;
3094 INT count = 0;
3095 BOOL write = FALSE;
3097 /* only refcount DLLs */
3098 if (comp->KeyPath == NULL ||
3099 comp->assembly ||
3100 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3101 comp->Attributes & msidbComponentAttributesODBCDataSource)
3102 write = FALSE;
3103 else
3105 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3106 write = (count > 0);
3108 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3109 write = TRUE;
3112 /* increment counts */
3113 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3115 ComponentList *cl;
3117 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3118 continue;
3120 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3122 if ( cl->component == comp )
3123 count++;
3127 /* decrement counts */
3128 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3130 ComponentList *cl;
3132 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3133 continue;
3135 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3137 if ( cl->component == comp )
3138 count--;
3142 /* ref count all the files in the component */
3143 if (write)
3145 MSIFILE *file;
3147 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3149 if (file->Component == comp)
3150 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3154 /* add a count for permanent */
3155 if (comp->Attributes & msidbComponentAttributesPermanent)
3156 count ++;
3158 comp->RefCount = count;
3160 if (write)
3161 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3164 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3166 if (comp->assembly)
3168 const WCHAR prefixW[] = {'<','\\',0};
3169 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3170 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3172 if (keypath)
3174 strcpyW( keypath, prefixW );
3175 strcatW( keypath, comp->assembly->display_name );
3177 return keypath;
3179 return resolve_keypath( package, comp );
3182 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3184 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3185 UINT rc;
3186 MSICOMPONENT *comp;
3187 HKEY hkey;
3189 TRACE("\n");
3191 squash_guid(package->ProductCode,squished_pc);
3192 msi_set_sourcedir_props(package, FALSE);
3194 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3196 MSIRECORD *uirow;
3197 INSTALLSTATE action;
3199 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3200 if (!comp->ComponentId)
3201 continue;
3203 squash_guid( comp->ComponentId, squished_cc );
3204 msi_free( comp->FullKeypath );
3205 comp->FullKeypath = build_full_keypath( package, comp );
3207 ACTION_RefCountComponent( package, comp );
3209 if (package->need_rollback) action = comp->Installed;
3210 else action = comp->ActionRequest;
3212 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3213 debugstr_w(comp->Component), debugstr_w(squished_cc),
3214 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3216 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3218 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3219 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3220 else
3221 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3223 if (rc != ERROR_SUCCESS)
3224 continue;
3226 if (comp->Attributes & msidbComponentAttributesPermanent)
3228 static const WCHAR szPermKey[] =
3229 { '0','0','0','0','0','0','0','0','0','0','0','0',
3230 '0','0','0','0','0','0','0','0','0','0','0','0',
3231 '0','0','0','0','0','0','0','0',0 };
3233 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3235 if (action == INSTALLSTATE_LOCAL)
3236 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3237 else
3239 MSIFILE *file;
3240 MSIRECORD *row;
3241 LPWSTR ptr, ptr2;
3242 WCHAR source[MAX_PATH];
3243 WCHAR base[MAX_PATH];
3244 LPWSTR sourcepath;
3246 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3247 static const WCHAR query[] = {
3248 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3249 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3250 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3251 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3252 '`','D','i','s','k','I','d','`',0};
3254 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3255 continue;
3257 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3258 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3259 ptr2 = strrchrW(source, '\\') + 1;
3260 msiobj_release(&row->hdr);
3262 lstrcpyW(base, package->PackagePath);
3263 ptr = strrchrW(base, '\\');
3264 *(ptr + 1) = '\0';
3266 sourcepath = msi_resolve_file_source(package, file);
3267 ptr = sourcepath + lstrlenW(base);
3268 lstrcpyW(ptr2, ptr);
3269 msi_free(sourcepath);
3271 msi_reg_set_val_str(hkey, squished_pc, source);
3273 RegCloseKey(hkey);
3275 else if (action == INSTALLSTATE_ABSENT)
3277 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3278 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3279 else
3280 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3283 /* UI stuff */
3284 uirow = MSI_CreateRecord(3);
3285 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3286 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3287 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3288 msi_ui_actiondata( package, szProcessComponents, uirow );
3289 msiobj_release( &uirow->hdr );
3291 return ERROR_SUCCESS;
3294 typedef struct {
3295 CLSID clsid;
3296 LPWSTR source;
3298 LPWSTR path;
3299 ITypeLib *ptLib;
3300 } typelib_struct;
3302 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3303 LPWSTR lpszName, LONG_PTR lParam)
3305 TLIBATTR *attr;
3306 typelib_struct *tl_struct = (typelib_struct*) lParam;
3307 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3308 int sz;
3309 HRESULT res;
3311 if (!IS_INTRESOURCE(lpszName))
3313 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3314 return TRUE;
3317 sz = strlenW(tl_struct->source)+4;
3318 sz *= sizeof(WCHAR);
3320 if ((INT_PTR)lpszName == 1)
3321 tl_struct->path = strdupW(tl_struct->source);
3322 else
3324 tl_struct->path = msi_alloc(sz);
3325 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3328 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3329 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3330 if (FAILED(res))
3332 msi_free(tl_struct->path);
3333 tl_struct->path = NULL;
3335 return TRUE;
3338 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3339 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3341 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3342 return FALSE;
3345 msi_free(tl_struct->path);
3346 tl_struct->path = NULL;
3348 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3349 ITypeLib_Release(tl_struct->ptLib);
3351 return TRUE;
3354 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3356 MSIPACKAGE* package = param;
3357 LPCWSTR component;
3358 MSICOMPONENT *comp;
3359 MSIFILE *file;
3360 typelib_struct tl_struct;
3361 ITypeLib *tlib;
3362 HMODULE module;
3363 HRESULT hr;
3365 component = MSI_RecordGetString(row,3);
3366 comp = msi_get_loaded_component(package,component);
3367 if (!comp)
3368 return ERROR_SUCCESS;
3370 comp->Action = msi_get_component_action( package, comp );
3371 if (comp->Action != INSTALLSTATE_LOCAL)
3373 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3374 return ERROR_SUCCESS;
3377 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3379 TRACE("component has no key path\n");
3380 return ERROR_SUCCESS;
3382 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3384 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3385 if (module)
3387 LPCWSTR guid;
3388 guid = MSI_RecordGetString(row,1);
3389 CLSIDFromString( guid, &tl_struct.clsid);
3390 tl_struct.source = strdupW( file->TargetPath );
3391 tl_struct.path = NULL;
3393 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3394 (LONG_PTR)&tl_struct);
3396 if (tl_struct.path)
3398 LPCWSTR helpid, help_path = NULL;
3399 HRESULT res;
3401 helpid = MSI_RecordGetString(row,6);
3403 if (helpid) help_path = msi_get_target_folder( package, helpid );
3404 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3406 if (FAILED(res))
3407 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3408 else
3409 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3411 ITypeLib_Release(tl_struct.ptLib);
3412 msi_free(tl_struct.path);
3414 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3416 FreeLibrary(module);
3417 msi_free(tl_struct.source);
3419 else
3421 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3422 if (FAILED(hr))
3424 ERR("Failed to load type library: %08x\n", hr);
3425 return ERROR_INSTALL_FAILURE;
3428 ITypeLib_Release(tlib);
3431 return ERROR_SUCCESS;
3434 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3436 static const WCHAR query[] = {
3437 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3438 '`','T','y','p','e','L','i','b','`',0};
3439 MSIQUERY *view;
3440 UINT rc;
3442 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3443 if (rc != ERROR_SUCCESS)
3444 return ERROR_SUCCESS;
3446 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3447 msiobj_release(&view->hdr);
3448 return rc;
3451 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3453 MSIPACKAGE *package = param;
3454 LPCWSTR component, guid;
3455 MSICOMPONENT *comp;
3456 GUID libid;
3457 UINT version;
3458 LCID language;
3459 SYSKIND syskind;
3460 HRESULT hr;
3462 component = MSI_RecordGetString( row, 3 );
3463 comp = msi_get_loaded_component( package, component );
3464 if (!comp)
3465 return ERROR_SUCCESS;
3467 comp->Action = msi_get_component_action( package, comp );
3468 if (comp->Action != INSTALLSTATE_ABSENT)
3470 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3471 return ERROR_SUCCESS;
3473 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3475 guid = MSI_RecordGetString( row, 1 );
3476 CLSIDFromString( guid, &libid );
3477 version = MSI_RecordGetInteger( row, 4 );
3478 language = MSI_RecordGetInteger( row, 2 );
3480 #ifdef _WIN64
3481 syskind = SYS_WIN64;
3482 #else
3483 syskind = SYS_WIN32;
3484 #endif
3486 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3487 if (FAILED(hr))
3489 WARN("Failed to unregister typelib: %08x\n", hr);
3492 return ERROR_SUCCESS;
3495 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3497 static const WCHAR query[] = {
3498 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3499 '`','T','y','p','e','L','i','b','`',0};
3500 MSIQUERY *view;
3501 UINT rc;
3503 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3504 if (rc != ERROR_SUCCESS)
3505 return ERROR_SUCCESS;
3507 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3508 msiobj_release( &view->hdr );
3509 return rc;
3512 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3514 static const WCHAR szlnk[] = {'.','l','n','k',0};
3515 LPCWSTR directory, extension, link_folder;
3516 LPWSTR link_file, filename;
3518 directory = MSI_RecordGetString( row, 2 );
3519 link_folder = msi_get_target_folder( package, directory );
3520 if (!link_folder)
3522 /* some installers use a separate root */
3523 MSIFOLDER *folder = msi_get_loaded_folder( package, directory );
3524 while (folder->Parent && strcmpW( folder->Parent, folder->Directory ))
3526 folder = msi_get_loaded_folder( package, folder->Parent );
3528 msi_resolve_target_folder( package, folder->Directory, TRUE );
3529 link_folder = msi_get_target_folder( package, directory );
3531 /* may be needed because of a bug somewhere else */
3532 msi_create_full_path( link_folder );
3534 filename = msi_dup_record_field( row, 3 );
3535 msi_reduce_to_long_filename( filename );
3537 extension = strchrW( filename, '.' );
3538 if (!extension || strcmpiW( extension, szlnk ))
3540 int len = strlenW( filename );
3541 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3542 memcpy( filename + len, szlnk, sizeof(szlnk) );
3544 link_file = msi_build_directory_name( 2, link_folder, filename );
3545 msi_free( filename );
3547 return link_file;
3550 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3552 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3553 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3554 WCHAR *folder, *dest, *path;
3556 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3557 folder = msi_dup_property( package->db, szWindowsFolder );
3558 else
3560 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3561 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3562 msi_free( appdata );
3564 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3565 msi_create_full_path( dest );
3566 path = msi_build_directory_name( 2, dest, icon_name );
3567 msi_free( folder );
3568 msi_free( dest );
3569 return path;
3572 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3574 MSIPACKAGE *package = param;
3575 LPWSTR link_file, deformated, path;
3576 LPCWSTR component, target;
3577 MSICOMPONENT *comp;
3578 IShellLinkW *sl = NULL;
3579 IPersistFile *pf = NULL;
3580 HRESULT res;
3582 component = MSI_RecordGetString(row, 4);
3583 comp = msi_get_loaded_component(package, component);
3584 if (!comp)
3585 return ERROR_SUCCESS;
3587 comp->Action = msi_get_component_action( package, comp );
3588 if (comp->Action != INSTALLSTATE_LOCAL)
3590 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3591 return ERROR_SUCCESS;
3593 msi_ui_actiondata( package, szCreateShortcuts, row );
3595 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3596 &IID_IShellLinkW, (LPVOID *) &sl );
3598 if (FAILED( res ))
3600 ERR("CLSID_ShellLink not available\n");
3601 goto err;
3604 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3605 if (FAILED( res ))
3607 ERR("QueryInterface(IID_IPersistFile) failed\n");
3608 goto err;
3611 target = MSI_RecordGetString(row, 5);
3612 if (strchrW(target, '['))
3614 int len;
3615 WCHAR *format_string, *p;
3617 if (!(p = strchrW( target, ']' ))) goto err;
3618 len = p - target + 1;
3619 format_string = msi_alloc( (len + 1) * sizeof(WCHAR) );
3620 memcpy( format_string, target, len * sizeof(WCHAR) );
3621 format_string[len] = 0;
3622 deformat_string( package, format_string, &deformated );
3623 msi_free( format_string );
3625 path = msi_alloc( (strlenW( deformated ) + strlenW( p + 1 ) + 2) * sizeof(WCHAR) );
3626 strcpyW( path, deformated );
3627 PathAddBackslashW( path );
3628 strcatW( path, p + 1 );
3629 TRACE("target path is %s\n", debugstr_w(path));
3631 IShellLinkW_SetPath( sl, path );
3632 msi_free( deformated );
3633 msi_free( path );
3635 else
3637 FIXME("poorly handled shortcut format, advertised shortcut\n");
3638 IShellLinkW_SetPath(sl,comp->FullKeypath);
3641 if (!MSI_RecordIsNull(row,6))
3643 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3644 deformat_string(package, arguments, &deformated);
3645 IShellLinkW_SetArguments(sl,deformated);
3646 msi_free(deformated);
3649 if (!MSI_RecordIsNull(row,7))
3651 LPCWSTR description = MSI_RecordGetString(row, 7);
3652 IShellLinkW_SetDescription(sl, description);
3655 if (!MSI_RecordIsNull(row,8))
3656 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3658 if (!MSI_RecordIsNull(row,9))
3660 INT index;
3661 LPCWSTR icon = MSI_RecordGetString(row, 9);
3663 path = msi_build_icon_path(package, icon);
3664 index = MSI_RecordGetInteger(row,10);
3666 /* no value means 0 */
3667 if (index == MSI_NULL_INTEGER)
3668 index = 0;
3670 IShellLinkW_SetIconLocation(sl, path, index);
3671 msi_free(path);
3674 if (!MSI_RecordIsNull(row,11))
3675 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3677 if (!MSI_RecordIsNull(row,12))
3679 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3680 full_path = msi_get_target_folder( package, wkdir );
3681 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3683 link_file = get_link_file(package, row);
3685 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3686 IPersistFile_Save(pf, link_file, FALSE);
3687 msi_free(link_file);
3689 err:
3690 if (pf)
3691 IPersistFile_Release( pf );
3692 if (sl)
3693 IShellLinkW_Release( sl );
3695 return ERROR_SUCCESS;
3698 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3700 static const WCHAR query[] = {
3701 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3702 '`','S','h','o','r','t','c','u','t','`',0};
3703 MSIQUERY *view;
3704 HRESULT res;
3705 UINT rc;
3707 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3708 if (rc != ERROR_SUCCESS)
3709 return ERROR_SUCCESS;
3711 res = CoInitialize( NULL );
3713 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3714 msiobj_release(&view->hdr);
3716 if (SUCCEEDED(res)) CoUninitialize();
3717 return rc;
3720 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3722 MSIPACKAGE *package = param;
3723 LPWSTR link_file;
3724 LPCWSTR component;
3725 MSICOMPONENT *comp;
3727 component = MSI_RecordGetString( row, 4 );
3728 comp = msi_get_loaded_component( package, component );
3729 if (!comp)
3730 return ERROR_SUCCESS;
3732 comp->Action = msi_get_component_action( package, comp );
3733 if (comp->Action != INSTALLSTATE_ABSENT)
3735 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3736 return ERROR_SUCCESS;
3738 msi_ui_actiondata( package, szRemoveShortcuts, row );
3740 link_file = get_link_file( package, row );
3742 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3743 if (!DeleteFileW( link_file ))
3745 WARN("Failed to remove shortcut file %u\n", GetLastError());
3747 msi_free( link_file );
3749 return ERROR_SUCCESS;
3752 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3754 static const WCHAR query[] = {
3755 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3756 '`','S','h','o','r','t','c','u','t','`',0};
3757 MSIQUERY *view;
3758 UINT rc;
3760 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3761 if (rc != ERROR_SUCCESS)
3762 return ERROR_SUCCESS;
3764 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3765 msiobj_release( &view->hdr );
3766 return rc;
3769 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3771 MSIPACKAGE* package = param;
3772 HANDLE the_file;
3773 LPWSTR FilePath;
3774 LPCWSTR FileName;
3775 CHAR buffer[1024];
3776 DWORD sz;
3777 UINT rc;
3779 FileName = MSI_RecordGetString(row,1);
3780 if (!FileName)
3782 ERR("Unable to get FileName\n");
3783 return ERROR_SUCCESS;
3786 FilePath = msi_build_icon_path(package, FileName);
3788 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3790 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3791 FILE_ATTRIBUTE_NORMAL, NULL);
3793 if (the_file == INVALID_HANDLE_VALUE)
3795 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3796 msi_free(FilePath);
3797 return ERROR_SUCCESS;
3802 DWORD write;
3803 sz = 1024;
3804 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3805 if (rc != ERROR_SUCCESS)
3807 ERR("Failed to get stream\n");
3808 CloseHandle(the_file);
3809 DeleteFileW(FilePath);
3810 break;
3812 WriteFile(the_file,buffer,sz,&write,NULL);
3813 } while (sz == 1024);
3815 msi_free(FilePath);
3816 CloseHandle(the_file);
3818 return ERROR_SUCCESS;
3821 static UINT msi_publish_icons(MSIPACKAGE *package)
3823 static const WCHAR query[]= {
3824 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3825 '`','I','c','o','n','`',0};
3826 MSIQUERY *view;
3827 UINT r;
3829 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3830 if (r == ERROR_SUCCESS)
3832 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3833 msiobj_release(&view->hdr);
3834 if (r != ERROR_SUCCESS)
3835 return r;
3837 return ERROR_SUCCESS;
3840 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3842 UINT r;
3843 HKEY source;
3844 LPWSTR buffer;
3845 MSIMEDIADISK *disk;
3846 MSISOURCELISTINFO *info;
3848 r = RegCreateKeyW(hkey, szSourceList, &source);
3849 if (r != ERROR_SUCCESS)
3850 return r;
3852 RegCloseKey(source);
3854 buffer = strrchrW(package->PackagePath, '\\') + 1;
3855 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3856 package->Context, MSICODE_PRODUCT,
3857 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3858 if (r != ERROR_SUCCESS)
3859 return r;
3861 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3862 package->Context, MSICODE_PRODUCT,
3863 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3864 if (r != ERROR_SUCCESS)
3865 return r;
3867 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3868 package->Context, MSICODE_PRODUCT,
3869 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3870 if (r != ERROR_SUCCESS)
3871 return r;
3873 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3875 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3876 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3877 info->options, info->value);
3878 else
3879 MsiSourceListSetInfoW(package->ProductCode, NULL,
3880 info->context, info->options,
3881 info->property, info->value);
3884 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3886 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3887 disk->context, disk->options,
3888 disk->disk_id, disk->volume_label, disk->disk_prompt);
3891 return ERROR_SUCCESS;
3894 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3896 MSIHANDLE hdb, suminfo;
3897 WCHAR guids[MAX_PATH];
3898 WCHAR packcode[SQUISH_GUID_SIZE];
3899 LPWSTR buffer;
3900 LPWSTR ptr;
3901 DWORD langid;
3902 DWORD size;
3903 UINT r;
3905 static const WCHAR szARPProductIcon[] =
3906 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3907 static const WCHAR szAssignment[] =
3908 {'A','s','s','i','g','n','m','e','n','t',0};
3909 static const WCHAR szAdvertiseFlags[] =
3910 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3911 static const WCHAR szClients[] =
3912 {'C','l','i','e','n','t','s',0};
3913 static const WCHAR szColon[] = {':',0};
3915 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3916 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3917 msi_free(buffer);
3919 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3920 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3922 /* FIXME */
3923 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3925 buffer = msi_dup_property(package->db, szARPProductIcon);
3926 if (buffer)
3928 LPWSTR path = msi_build_icon_path(package, buffer);
3929 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3930 msi_free(path);
3931 msi_free(buffer);
3934 buffer = msi_dup_property(package->db, szProductVersion);
3935 if (buffer)
3937 DWORD verdword = msi_version_str_to_dword(buffer);
3938 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3939 msi_free(buffer);
3942 msi_reg_set_val_dword(hkey, szAssignment, 0);
3943 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3944 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3945 msi_reg_set_val_str(hkey, szClients, szColon);
3947 hdb = alloc_msihandle(&package->db->hdr);
3948 if (!hdb)
3949 return ERROR_NOT_ENOUGH_MEMORY;
3951 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3952 MsiCloseHandle(hdb);
3953 if (r != ERROR_SUCCESS)
3954 goto done;
3956 size = MAX_PATH;
3957 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3958 NULL, guids, &size);
3959 if (r != ERROR_SUCCESS)
3960 goto done;
3962 ptr = strchrW(guids, ';');
3963 if (ptr) *ptr = 0;
3964 squash_guid(guids, packcode);
3965 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3967 done:
3968 MsiCloseHandle(suminfo);
3969 return ERROR_SUCCESS;
3972 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3974 UINT r;
3975 HKEY hkey;
3976 LPWSTR upgrade;
3977 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3979 upgrade = msi_dup_property(package->db, szUpgradeCode);
3980 if (!upgrade)
3981 return ERROR_SUCCESS;
3983 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3984 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3985 else
3986 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3988 if (r != ERROR_SUCCESS)
3990 WARN("failed to open upgrade code key\n");
3991 msi_free(upgrade);
3992 return ERROR_SUCCESS;
3994 squash_guid(package->ProductCode, squashed_pc);
3995 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3996 RegCloseKey(hkey);
3997 msi_free(upgrade);
3998 return ERROR_SUCCESS;
4001 static BOOL msi_check_publish(MSIPACKAGE *package)
4003 MSIFEATURE *feature;
4005 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4007 feature->Action = msi_get_feature_action( package, feature );
4008 if (feature->Action == INSTALLSTATE_LOCAL)
4009 return TRUE;
4012 return FALSE;
4015 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4017 MSIFEATURE *feature;
4019 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4021 feature->Action = msi_get_feature_action( package, feature );
4022 if (feature->Action != INSTALLSTATE_ABSENT)
4023 return FALSE;
4026 return TRUE;
4029 static UINT msi_publish_patches( MSIPACKAGE *package )
4031 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4032 WCHAR patch_squashed[GUID_SIZE];
4033 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4034 LONG res;
4035 MSIPATCHINFO *patch;
4036 UINT r;
4037 WCHAR *p, *all_patches = NULL;
4038 DWORD len = 0;
4040 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4041 if (r != ERROR_SUCCESS)
4042 return ERROR_FUNCTION_FAILED;
4044 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4045 if (res != ERROR_SUCCESS)
4047 r = ERROR_FUNCTION_FAILED;
4048 goto done;
4051 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4052 if (r != ERROR_SUCCESS)
4053 goto done;
4055 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4057 squash_guid( patch->patchcode, patch_squashed );
4058 len += strlenW( patch_squashed ) + 1;
4061 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4062 if (!all_patches)
4063 goto done;
4065 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4067 HKEY patch_key;
4069 squash_guid( patch->patchcode, p );
4070 p += strlenW( p ) + 1;
4072 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4073 (const BYTE *)patch->transforms,
4074 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4075 if (res != ERROR_SUCCESS)
4076 goto done;
4078 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4079 if (r != ERROR_SUCCESS)
4080 goto done;
4082 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4083 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4084 RegCloseKey( patch_key );
4085 if (res != ERROR_SUCCESS)
4086 goto done;
4088 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4090 res = GetLastError();
4091 ERR("Unable to copy patch package %d\n", res);
4092 goto done;
4094 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4095 if (res != ERROR_SUCCESS)
4096 goto done;
4098 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4099 RegCloseKey( patch_key );
4100 if (res != ERROR_SUCCESS)
4101 goto done;
4104 all_patches[len] = 0;
4105 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4106 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4107 if (res != ERROR_SUCCESS)
4108 goto done;
4110 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4111 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4112 if (res != ERROR_SUCCESS)
4113 r = ERROR_FUNCTION_FAILED;
4115 done:
4116 RegCloseKey( product_patches_key );
4117 RegCloseKey( patches_key );
4118 RegCloseKey( product_key );
4119 msi_free( all_patches );
4120 return r;
4123 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4125 UINT rc;
4126 HKEY hukey = NULL, hudkey = NULL;
4127 MSIRECORD *uirow;
4129 if (!list_empty(&package->patches))
4131 rc = msi_publish_patches(package);
4132 if (rc != ERROR_SUCCESS)
4133 goto end;
4136 /* FIXME: also need to publish if the product is in advertise mode */
4137 if (!msi_check_publish(package))
4138 return ERROR_SUCCESS;
4140 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4141 &hukey, TRUE);
4142 if (rc != ERROR_SUCCESS)
4143 goto end;
4145 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4146 NULL, &hudkey, TRUE);
4147 if (rc != ERROR_SUCCESS)
4148 goto end;
4150 rc = msi_publish_upgrade_code(package);
4151 if (rc != ERROR_SUCCESS)
4152 goto end;
4154 rc = msi_publish_product_properties(package, hukey);
4155 if (rc != ERROR_SUCCESS)
4156 goto end;
4158 rc = msi_publish_sourcelist(package, hukey);
4159 if (rc != ERROR_SUCCESS)
4160 goto end;
4162 rc = msi_publish_icons(package);
4164 end:
4165 uirow = MSI_CreateRecord( 1 );
4166 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4167 msi_ui_actiondata( package, szPublishProduct, uirow );
4168 msiobj_release( &uirow->hdr );
4170 RegCloseKey(hukey);
4171 RegCloseKey(hudkey);
4172 return rc;
4175 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4177 WCHAR *filename, *ptr, *folder, *ret;
4178 const WCHAR *dirprop;
4180 filename = msi_dup_record_field( row, 2 );
4181 if (filename && (ptr = strchrW( filename, '|' )))
4182 ptr++;
4183 else
4184 ptr = filename;
4186 dirprop = MSI_RecordGetString( row, 3 );
4187 if (dirprop)
4189 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4190 if (!folder) folder = msi_dup_property( package->db, dirprop );
4192 else
4193 folder = msi_dup_property( package->db, szWindowsFolder );
4195 if (!folder)
4197 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4198 msi_free( filename );
4199 return NULL;
4202 ret = msi_build_directory_name( 2, folder, ptr );
4204 msi_free( filename );
4205 msi_free( folder );
4206 return ret;
4209 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4211 MSIPACKAGE *package = param;
4212 LPCWSTR component, section, key, value, identifier;
4213 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4214 MSIRECORD * uirow;
4215 INT action;
4216 MSICOMPONENT *comp;
4218 component = MSI_RecordGetString(row, 8);
4219 comp = msi_get_loaded_component(package,component);
4220 if (!comp)
4221 return ERROR_SUCCESS;
4223 comp->Action = msi_get_component_action( package, comp );
4224 if (comp->Action != INSTALLSTATE_LOCAL)
4226 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4227 return ERROR_SUCCESS;
4230 identifier = MSI_RecordGetString(row,1);
4231 section = MSI_RecordGetString(row,4);
4232 key = MSI_RecordGetString(row,5);
4233 value = MSI_RecordGetString(row,6);
4234 action = MSI_RecordGetInteger(row,7);
4236 deformat_string(package,section,&deformated_section);
4237 deformat_string(package,key,&deformated_key);
4238 deformat_string(package,value,&deformated_value);
4240 fullname = get_ini_file_name(package, row);
4242 if (action == 0)
4244 TRACE("Adding value %s to section %s in %s\n",
4245 debugstr_w(deformated_key), debugstr_w(deformated_section),
4246 debugstr_w(fullname));
4247 WritePrivateProfileStringW(deformated_section, deformated_key,
4248 deformated_value, fullname);
4250 else if (action == 1)
4252 WCHAR returned[10];
4253 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4254 returned, 10, fullname);
4255 if (returned[0] == 0)
4257 TRACE("Adding value %s to section %s in %s\n",
4258 debugstr_w(deformated_key), debugstr_w(deformated_section),
4259 debugstr_w(fullname));
4261 WritePrivateProfileStringW(deformated_section, deformated_key,
4262 deformated_value, fullname);
4265 else if (action == 3)
4266 FIXME("Append to existing section not yet implemented\n");
4268 uirow = MSI_CreateRecord(4);
4269 MSI_RecordSetStringW(uirow,1,identifier);
4270 MSI_RecordSetStringW(uirow,2,deformated_section);
4271 MSI_RecordSetStringW(uirow,3,deformated_key);
4272 MSI_RecordSetStringW(uirow,4,deformated_value);
4273 msi_ui_actiondata( package, szWriteIniValues, uirow );
4274 msiobj_release( &uirow->hdr );
4276 msi_free(fullname);
4277 msi_free(deformated_key);
4278 msi_free(deformated_value);
4279 msi_free(deformated_section);
4280 return ERROR_SUCCESS;
4283 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4285 static const WCHAR query[] = {
4286 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4287 '`','I','n','i','F','i','l','e','`',0};
4288 MSIQUERY *view;
4289 UINT rc;
4291 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4292 if (rc != ERROR_SUCCESS)
4293 return ERROR_SUCCESS;
4295 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4296 msiobj_release(&view->hdr);
4297 return rc;
4300 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4302 MSIPACKAGE *package = param;
4303 LPCWSTR component, section, key, value, identifier;
4304 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4305 MSICOMPONENT *comp;
4306 MSIRECORD *uirow;
4307 INT action;
4309 component = MSI_RecordGetString( row, 8 );
4310 comp = msi_get_loaded_component( package, component );
4311 if (!comp)
4312 return ERROR_SUCCESS;
4314 comp->Action = msi_get_component_action( package, comp );
4315 if (comp->Action != INSTALLSTATE_ABSENT)
4317 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4318 return ERROR_SUCCESS;
4321 identifier = MSI_RecordGetString( row, 1 );
4322 section = MSI_RecordGetString( row, 4 );
4323 key = MSI_RecordGetString( row, 5 );
4324 value = MSI_RecordGetString( row, 6 );
4325 action = MSI_RecordGetInteger( row, 7 );
4327 deformat_string( package, section, &deformated_section );
4328 deformat_string( package, key, &deformated_key );
4329 deformat_string( package, value, &deformated_value );
4331 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4333 filename = get_ini_file_name( package, row );
4335 TRACE("Removing key %s from section %s in %s\n",
4336 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4338 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4340 WARN("Unable to remove key %u\n", GetLastError());
4342 msi_free( filename );
4344 else
4345 FIXME("Unsupported action %d\n", action);
4348 uirow = MSI_CreateRecord( 4 );
4349 MSI_RecordSetStringW( uirow, 1, identifier );
4350 MSI_RecordSetStringW( uirow, 2, deformated_section );
4351 MSI_RecordSetStringW( uirow, 3, deformated_key );
4352 MSI_RecordSetStringW( uirow, 4, deformated_value );
4353 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4354 msiobj_release( &uirow->hdr );
4356 msi_free( deformated_key );
4357 msi_free( deformated_value );
4358 msi_free( deformated_section );
4359 return ERROR_SUCCESS;
4362 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4364 MSIPACKAGE *package = param;
4365 LPCWSTR component, section, key, value, identifier;
4366 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4367 MSICOMPONENT *comp;
4368 MSIRECORD *uirow;
4369 INT action;
4371 component = MSI_RecordGetString( row, 8 );
4372 comp = msi_get_loaded_component( package, component );
4373 if (!comp)
4374 return ERROR_SUCCESS;
4376 comp->Action = msi_get_component_action( package, comp );
4377 if (comp->Action != INSTALLSTATE_LOCAL)
4379 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4380 return ERROR_SUCCESS;
4383 identifier = MSI_RecordGetString( row, 1 );
4384 section = MSI_RecordGetString( row, 4 );
4385 key = MSI_RecordGetString( row, 5 );
4386 value = MSI_RecordGetString( row, 6 );
4387 action = MSI_RecordGetInteger( row, 7 );
4389 deformat_string( package, section, &deformated_section );
4390 deformat_string( package, key, &deformated_key );
4391 deformat_string( package, value, &deformated_value );
4393 if (action == msidbIniFileActionRemoveLine)
4395 filename = get_ini_file_name( package, row );
4397 TRACE("Removing key %s from section %s in %s\n",
4398 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4400 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4402 WARN("Unable to remove key %u\n", GetLastError());
4404 msi_free( filename );
4406 else
4407 FIXME("Unsupported action %d\n", action);
4409 uirow = MSI_CreateRecord( 4 );
4410 MSI_RecordSetStringW( uirow, 1, identifier );
4411 MSI_RecordSetStringW( uirow, 2, deformated_section );
4412 MSI_RecordSetStringW( uirow, 3, deformated_key );
4413 MSI_RecordSetStringW( uirow, 4, deformated_value );
4414 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4415 msiobj_release( &uirow->hdr );
4417 msi_free( deformated_key );
4418 msi_free( deformated_value );
4419 msi_free( deformated_section );
4420 return ERROR_SUCCESS;
4423 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4425 static const WCHAR query[] = {
4426 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4427 '`','I','n','i','F','i','l','e','`',0};
4428 static const WCHAR remove_query[] = {
4429 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4430 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4431 MSIQUERY *view;
4432 UINT rc;
4434 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4435 if (rc == ERROR_SUCCESS)
4437 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4438 msiobj_release( &view->hdr );
4439 if (rc != ERROR_SUCCESS)
4440 return rc;
4442 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4443 if (rc == ERROR_SUCCESS)
4445 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4446 msiobj_release( &view->hdr );
4447 if (rc != ERROR_SUCCESS)
4448 return rc;
4450 return ERROR_SUCCESS;
4453 static void register_dll( const WCHAR *dll, BOOL unregister )
4455 HMODULE hmod;
4457 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4458 if (hmod)
4460 HRESULT (WINAPI *func_ptr)( void );
4461 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4463 func_ptr = (void *)GetProcAddress( hmod, func );
4464 if (func_ptr)
4466 HRESULT hr = func_ptr();
4467 if (FAILED( hr ))
4468 WARN("failed to register dll 0x%08x\n", hr);
4470 else
4471 WARN("entry point %s not found\n", func);
4472 FreeLibrary( hmod );
4473 return;
4475 WARN("failed to load library %u\n", GetLastError());
4478 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4480 MSIPACKAGE *package = param;
4481 LPCWSTR filename;
4482 MSIFILE *file;
4483 MSIRECORD *uirow;
4485 filename = MSI_RecordGetString(row,1);
4486 file = msi_get_loaded_file( package, filename );
4487 if (!file)
4489 WARN("unable to find file %s\n", debugstr_w(filename));
4490 return ERROR_SUCCESS;
4492 file->Component->Action = msi_get_component_action( package, file->Component );
4493 if (file->Component->Action != INSTALLSTATE_LOCAL)
4495 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4496 return ERROR_SUCCESS;
4499 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4500 register_dll( file->TargetPath, FALSE );
4502 uirow = MSI_CreateRecord( 2 );
4503 MSI_RecordSetStringW( uirow, 1, filename );
4504 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4505 msi_ui_actiondata( package, szSelfRegModules, uirow );
4506 msiobj_release( &uirow->hdr );
4508 return ERROR_SUCCESS;
4511 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4513 static const WCHAR query[] = {
4514 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4515 '`','S','e','l','f','R','e','g','`',0};
4516 MSIQUERY *view;
4517 UINT rc;
4519 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4520 if (rc != ERROR_SUCCESS)
4521 return ERROR_SUCCESS;
4523 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4524 msiobj_release(&view->hdr);
4525 return rc;
4528 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4530 MSIPACKAGE *package = param;
4531 LPCWSTR filename;
4532 MSIFILE *file;
4533 MSIRECORD *uirow;
4535 filename = MSI_RecordGetString( row, 1 );
4536 file = msi_get_loaded_file( package, filename );
4537 if (!file)
4539 WARN("unable to find file %s\n", debugstr_w(filename));
4540 return ERROR_SUCCESS;
4542 file->Component->Action = msi_get_component_action( package, file->Component );
4543 if (file->Component->Action != INSTALLSTATE_ABSENT)
4545 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4546 return ERROR_SUCCESS;
4549 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4550 register_dll( file->TargetPath, TRUE );
4552 uirow = MSI_CreateRecord( 2 );
4553 MSI_RecordSetStringW( uirow, 1, filename );
4554 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4555 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4556 msiobj_release( &uirow->hdr );
4558 return ERROR_SUCCESS;
4561 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4563 static const WCHAR query[] = {
4564 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4565 '`','S','e','l','f','R','e','g','`',0};
4566 MSIQUERY *view;
4567 UINT rc;
4569 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4570 if (rc != ERROR_SUCCESS)
4571 return ERROR_SUCCESS;
4573 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4574 msiobj_release( &view->hdr );
4575 return rc;
4578 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4580 MSIFEATURE *feature;
4581 UINT rc;
4582 HKEY hkey = NULL, userdata = NULL;
4584 if (!msi_check_publish(package))
4585 return ERROR_SUCCESS;
4587 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4588 &hkey, TRUE);
4589 if (rc != ERROR_SUCCESS)
4590 goto end;
4592 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4593 &userdata, TRUE);
4594 if (rc != ERROR_SUCCESS)
4595 goto end;
4597 /* here the guids are base 85 encoded */
4598 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4600 ComponentList *cl;
4601 LPWSTR data = NULL;
4602 GUID clsid;
4603 INT size;
4604 BOOL absent = FALSE;
4605 MSIRECORD *uirow;
4607 if (feature->Action != INSTALLSTATE_LOCAL &&
4608 feature->Action != INSTALLSTATE_SOURCE &&
4609 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4611 size = 1;
4612 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4614 size += 21;
4616 if (feature->Feature_Parent)
4617 size += strlenW( feature->Feature_Parent )+2;
4619 data = msi_alloc(size * sizeof(WCHAR));
4621 data[0] = 0;
4622 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4624 MSICOMPONENT* component = cl->component;
4625 WCHAR buf[21];
4627 buf[0] = 0;
4628 if (component->ComponentId)
4630 TRACE("From %s\n",debugstr_w(component->ComponentId));
4631 CLSIDFromString(component->ComponentId, &clsid);
4632 encode_base85_guid(&clsid,buf);
4633 TRACE("to %s\n",debugstr_w(buf));
4634 strcatW(data,buf);
4638 if (feature->Feature_Parent)
4640 static const WCHAR sep[] = {'\2',0};
4641 strcatW(data,sep);
4642 strcatW(data,feature->Feature_Parent);
4645 msi_reg_set_val_str( userdata, feature->Feature, data );
4646 msi_free(data);
4648 size = 0;
4649 if (feature->Feature_Parent)
4650 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4651 if (!absent)
4653 size += sizeof(WCHAR);
4654 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4655 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4657 else
4659 size += 2*sizeof(WCHAR);
4660 data = msi_alloc(size);
4661 data[0] = 0x6;
4662 data[1] = 0;
4663 if (feature->Feature_Parent)
4664 strcpyW( &data[1], feature->Feature_Parent );
4665 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4666 (LPBYTE)data,size);
4667 msi_free(data);
4670 /* the UI chunk */
4671 uirow = MSI_CreateRecord( 1 );
4672 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4673 msi_ui_actiondata( package, szPublishFeatures, uirow );
4674 msiobj_release( &uirow->hdr );
4675 /* FIXME: call msi_ui_progress? */
4678 end:
4679 RegCloseKey(hkey);
4680 RegCloseKey(userdata);
4681 return rc;
4684 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4686 UINT r;
4687 HKEY hkey;
4688 MSIRECORD *uirow;
4690 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4692 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4693 &hkey, FALSE);
4694 if (r == ERROR_SUCCESS)
4696 RegDeleteValueW(hkey, feature->Feature);
4697 RegCloseKey(hkey);
4700 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4701 &hkey, FALSE);
4702 if (r == ERROR_SUCCESS)
4704 RegDeleteValueW(hkey, feature->Feature);
4705 RegCloseKey(hkey);
4708 uirow = MSI_CreateRecord( 1 );
4709 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4710 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4711 msiobj_release( &uirow->hdr );
4713 return ERROR_SUCCESS;
4716 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4718 MSIFEATURE *feature;
4720 if (!msi_check_unpublish(package))
4721 return ERROR_SUCCESS;
4723 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4725 msi_unpublish_feature(package, feature);
4728 return ERROR_SUCCESS;
4731 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4733 SYSTEMTIME systime;
4734 DWORD size, langid;
4735 WCHAR date[9], *val, *buffer;
4736 const WCHAR *prop, *key;
4738 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4739 static const WCHAR modpath_fmt[] =
4740 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4741 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4742 static const WCHAR szModifyPath[] =
4743 {'M','o','d','i','f','y','P','a','t','h',0};
4744 static const WCHAR szUninstallString[] =
4745 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4746 static const WCHAR szEstimatedSize[] =
4747 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4748 static const WCHAR szDisplayVersion[] =
4749 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4750 static const WCHAR szInstallSource[] =
4751 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4752 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4753 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4754 static const WCHAR szAuthorizedCDFPrefix[] =
4755 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4756 static const WCHAR szARPCONTACT[] =
4757 {'A','R','P','C','O','N','T','A','C','T',0};
4758 static const WCHAR szContact[] =
4759 {'C','o','n','t','a','c','t',0};
4760 static const WCHAR szARPCOMMENTS[] =
4761 {'A','R','P','C','O','M','M','E','N','T','S',0};
4762 static const WCHAR szComments[] =
4763 {'C','o','m','m','e','n','t','s',0};
4764 static const WCHAR szProductName[] =
4765 {'P','r','o','d','u','c','t','N','a','m','e',0};
4766 static const WCHAR szDisplayName[] =
4767 {'D','i','s','p','l','a','y','N','a','m','e',0};
4768 static const WCHAR szARPHELPLINK[] =
4769 {'A','R','P','H','E','L','P','L','I','N','K',0};
4770 static const WCHAR szHelpLink[] =
4771 {'H','e','l','p','L','i','n','k',0};
4772 static const WCHAR szARPHELPTELEPHONE[] =
4773 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4774 static const WCHAR szHelpTelephone[] =
4775 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4776 static const WCHAR szARPINSTALLLOCATION[] =
4777 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4778 static const WCHAR szInstallLocation[] =
4779 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4780 static const WCHAR szManufacturer[] =
4781 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4782 static const WCHAR szPublisher[] =
4783 {'P','u','b','l','i','s','h','e','r',0};
4784 static const WCHAR szARPREADME[] =
4785 {'A','R','P','R','E','A','D','M','E',0};
4786 static const WCHAR szReadme[] =
4787 {'R','e','a','d','M','e',0};
4788 static const WCHAR szARPSIZE[] =
4789 {'A','R','P','S','I','Z','E',0};
4790 static const WCHAR szSize[] =
4791 {'S','i','z','e',0};
4792 static const WCHAR szARPURLINFOABOUT[] =
4793 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4794 static const WCHAR szURLInfoAbout[] =
4795 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4796 static const WCHAR szARPURLUPDATEINFO[] =
4797 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4798 static const WCHAR szURLUpdateInfo[] =
4799 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4800 static const WCHAR szARPSYSTEMCOMPONENT[] =
4801 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4802 static const WCHAR szSystemComponent[] =
4803 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4805 static const WCHAR *propval[] = {
4806 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4807 szARPCONTACT, szContact,
4808 szARPCOMMENTS, szComments,
4809 szProductName, szDisplayName,
4810 szARPHELPLINK, szHelpLink,
4811 szARPHELPTELEPHONE, szHelpTelephone,
4812 szARPINSTALLLOCATION, szInstallLocation,
4813 szSourceDir, szInstallSource,
4814 szManufacturer, szPublisher,
4815 szARPREADME, szReadme,
4816 szARPSIZE, szSize,
4817 szARPURLINFOABOUT, szURLInfoAbout,
4818 szARPURLUPDATEINFO, szURLUpdateInfo,
4819 NULL
4821 const WCHAR **p = propval;
4823 while (*p)
4825 prop = *p++;
4826 key = *p++;
4827 val = msi_dup_property(package->db, prop);
4828 msi_reg_set_val_str(hkey, key, val);
4829 msi_free(val);
4832 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4833 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4835 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4837 size = deformat_string(package, modpath_fmt, &buffer);
4838 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4839 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4840 msi_free(buffer);
4842 /* FIXME: Write real Estimated Size when we have it */
4843 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4845 GetLocalTime(&systime);
4846 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4847 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4849 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4850 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4852 buffer = msi_dup_property(package->db, szProductVersion);
4853 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4854 if (buffer)
4856 DWORD verdword = msi_version_str_to_dword(buffer);
4858 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4859 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4860 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4861 msi_free(buffer);
4864 return ERROR_SUCCESS;
4867 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4869 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4870 MSIRECORD *uirow;
4871 LPWSTR upgrade_code;
4872 HKEY hkey, props, upgrade_key;
4873 UINT rc;
4875 /* FIXME: also need to publish if the product is in advertise mode */
4876 if (!msi_check_publish(package))
4877 return ERROR_SUCCESS;
4879 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4880 if (rc != ERROR_SUCCESS)
4881 return rc;
4883 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4884 if (rc != ERROR_SUCCESS)
4885 goto done;
4887 rc = msi_publish_install_properties(package, hkey);
4888 if (rc != ERROR_SUCCESS)
4889 goto done;
4891 rc = msi_publish_install_properties(package, props);
4892 if (rc != ERROR_SUCCESS)
4893 goto done;
4895 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4896 if (upgrade_code)
4898 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4899 if (rc == ERROR_SUCCESS)
4901 squash_guid( package->ProductCode, squashed_pc );
4902 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4903 RegCloseKey( upgrade_key );
4905 msi_free( upgrade_code );
4907 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4908 package->delete_on_close = FALSE;
4910 done:
4911 uirow = MSI_CreateRecord( 1 );
4912 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4913 msi_ui_actiondata( package, szRegisterProduct, uirow );
4914 msiobj_release( &uirow->hdr );
4916 RegCloseKey(hkey);
4917 return ERROR_SUCCESS;
4920 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4922 return execute_script(package,INSTALL_SCRIPT);
4925 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4927 MSIPACKAGE *package = param;
4928 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4929 WCHAR *p, *icon_path;
4931 if (!icon) return ERROR_SUCCESS;
4932 if ((icon_path = msi_build_icon_path( package, icon )))
4934 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4935 DeleteFileW( icon_path );
4936 if ((p = strrchrW( icon_path, '\\' )))
4938 *p = 0;
4939 RemoveDirectoryW( icon_path );
4941 msi_free( icon_path );
4943 return ERROR_SUCCESS;
4946 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4948 static const WCHAR query[]= {
4949 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4950 MSIQUERY *view;
4951 UINT r;
4953 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4954 if (r == ERROR_SUCCESS)
4956 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4957 msiobj_release( &view->hdr );
4958 if (r != ERROR_SUCCESS)
4959 return r;
4961 return ERROR_SUCCESS;
4964 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4966 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4967 WCHAR *upgrade, **features;
4968 BOOL full_uninstall = TRUE;
4969 MSIFEATURE *feature;
4970 MSIPATCHINFO *patch;
4971 UINT i;
4973 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4975 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4977 features = msi_split_string( remove, ',' );
4978 for (i = 0; features && features[i]; i++)
4980 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4982 msi_free(features);
4984 if (!full_uninstall)
4985 return ERROR_SUCCESS;
4987 MSIREG_DeleteProductKey(package->ProductCode);
4988 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4989 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4991 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4992 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4993 MSIREG_DeleteUserProductKey(package->ProductCode);
4994 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4996 upgrade = msi_dup_property(package->db, szUpgradeCode);
4997 if (upgrade)
4999 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5000 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5001 msi_free(upgrade);
5004 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5006 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5007 if (!strcmpW( package->ProductCode, patch->products ))
5009 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5010 patch->delete_on_close = TRUE;
5012 /* FIXME: remove local patch package if this is the last product */
5014 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5015 package->delete_on_close = TRUE;
5017 msi_unpublish_icons( package );
5018 return ERROR_SUCCESS;
5021 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5023 UINT rc;
5024 WCHAR *remove;
5026 /* turn off scheduling */
5027 package->script->CurrentlyScripting= FALSE;
5029 /* first do the same as an InstallExecute */
5030 rc = ACTION_InstallExecute(package);
5031 if (rc != ERROR_SUCCESS)
5032 return rc;
5034 /* then handle Commit Actions */
5035 rc = execute_script(package,COMMIT_SCRIPT);
5036 if (rc != ERROR_SUCCESS)
5037 return rc;
5039 remove = msi_dup_property(package->db, szRemove);
5040 rc = msi_unpublish_product(package, remove);
5041 msi_free(remove);
5042 return rc;
5045 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5047 static const WCHAR RunOnce[] = {
5048 'S','o','f','t','w','a','r','e','\\',
5049 'M','i','c','r','o','s','o','f','t','\\',
5050 'W','i','n','d','o','w','s','\\',
5051 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5052 'R','u','n','O','n','c','e',0};
5053 static const WCHAR InstallRunOnce[] = {
5054 'S','o','f','t','w','a','r','e','\\',
5055 'M','i','c','r','o','s','o','f','t','\\',
5056 'W','i','n','d','o','w','s','\\',
5057 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5058 'I','n','s','t','a','l','l','e','r','\\',
5059 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5061 static const WCHAR msiexec_fmt[] = {
5062 '%','s',
5063 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5064 '\"','%','s','\"',0};
5065 static const WCHAR install_fmt[] = {
5066 '/','I',' ','\"','%','s','\"',' ',
5067 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5068 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5069 WCHAR buffer[256], sysdir[MAX_PATH];
5070 HKEY hkey;
5071 WCHAR squished_pc[100];
5073 squash_guid(package->ProductCode,squished_pc);
5075 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5076 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5077 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5078 squished_pc);
5080 msi_reg_set_val_str( hkey, squished_pc, buffer );
5081 RegCloseKey(hkey);
5083 TRACE("Reboot command %s\n",debugstr_w(buffer));
5085 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5086 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5088 msi_reg_set_val_str( hkey, squished_pc, buffer );
5089 RegCloseKey(hkey);
5091 return ERROR_INSTALL_SUSPEND;
5094 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5096 static const WCHAR query[] =
5097 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5098 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5099 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5100 MSIRECORD *rec, *row;
5101 DWORD i, size = 0;
5102 va_list va;
5103 const WCHAR *str;
5104 WCHAR *data;
5106 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5108 rec = MSI_CreateRecord( count + 2 );
5109 str = MSI_RecordGetString( row, 1 );
5110 MSI_RecordSetStringW( rec, 0, str );
5111 msiobj_release( &row->hdr );
5112 MSI_RecordSetInteger( rec, 1, error );
5114 va_start( va, count );
5115 for (i = 0; i < count; i++)
5117 str = va_arg( va, const WCHAR *);
5118 MSI_RecordSetStringW( rec, i + 2, str );
5120 va_end( va );
5122 MSI_FormatRecordW( package, rec, NULL, &size );
5123 size++;
5124 data = msi_alloc( size * sizeof(WCHAR) );
5125 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5126 else data[0] = 0;
5127 msiobj_release( &rec->hdr );
5128 return data;
5131 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5133 DWORD attrib;
5134 UINT rc;
5137 * We are currently doing what should be done here in the top level Install
5138 * however for Administrative and uninstalls this step will be needed
5140 if (!package->PackagePath)
5141 return ERROR_SUCCESS;
5143 msi_set_sourcedir_props(package, TRUE);
5145 attrib = GetFileAttributesW(package->db->path);
5146 if (attrib == INVALID_FILE_ATTRIBUTES)
5148 LPWSTR prompt, msg;
5149 DWORD size = 0;
5151 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5152 package->Context, MSICODE_PRODUCT,
5153 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5154 if (rc == ERROR_MORE_DATA)
5156 prompt = msi_alloc(size * sizeof(WCHAR));
5157 MsiSourceListGetInfoW(package->ProductCode, NULL,
5158 package->Context, MSICODE_PRODUCT,
5159 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5161 else
5162 prompt = strdupW(package->db->path);
5164 msg = msi_build_error_string(package, 1302, 1, prompt);
5165 msi_free(prompt);
5166 while(attrib == INVALID_FILE_ATTRIBUTES)
5168 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5169 if (rc == IDCANCEL)
5171 msi_free(msg);
5172 return ERROR_INSTALL_USEREXIT;
5174 attrib = GetFileAttributesW(package->db->path);
5176 msi_free(msg);
5177 rc = ERROR_SUCCESS;
5179 else
5180 return ERROR_SUCCESS;
5182 return rc;
5185 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5187 HKEY hkey = 0;
5188 LPWSTR buffer, productid = NULL;
5189 UINT i, rc = ERROR_SUCCESS;
5190 MSIRECORD *uirow;
5192 static const WCHAR szPropKeys[][80] =
5194 {'P','r','o','d','u','c','t','I','D',0},
5195 {'U','S','E','R','N','A','M','E',0},
5196 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5197 {0},
5200 static const WCHAR szRegKeys[][80] =
5202 {'P','r','o','d','u','c','t','I','D',0},
5203 {'R','e','g','O','w','n','e','r',0},
5204 {'R','e','g','C','o','m','p','a','n','y',0},
5205 {0},
5208 if (msi_check_unpublish(package))
5210 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5211 goto end;
5214 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5215 if (!productid)
5216 goto end;
5218 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5219 NULL, &hkey, TRUE);
5220 if (rc != ERROR_SUCCESS)
5221 goto end;
5223 for( i = 0; szPropKeys[i][0]; i++ )
5225 buffer = msi_dup_property( package->db, szPropKeys[i] );
5226 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5227 msi_free( buffer );
5230 end:
5231 uirow = MSI_CreateRecord( 1 );
5232 MSI_RecordSetStringW( uirow, 1, productid );
5233 msi_ui_actiondata( package, szRegisterUser, uirow );
5234 msiobj_release( &uirow->hdr );
5236 msi_free(productid);
5237 RegCloseKey(hkey);
5238 return rc;
5242 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5244 UINT rc;
5246 package->script->InWhatSequence |= SEQUENCE_EXEC;
5247 rc = ACTION_ProcessExecSequence(package,FALSE);
5248 return rc;
5251 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5253 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5254 WCHAR productid_85[21], component_85[21], *ret;
5255 GUID clsid;
5256 DWORD sz;
5258 /* > is used if there is a component GUID and < if not. */
5260 productid_85[0] = 0;
5261 component_85[0] = 0;
5262 CLSIDFromString( package->ProductCode, &clsid );
5264 encode_base85_guid( &clsid, productid_85 );
5265 if (component)
5267 CLSIDFromString( component->ComponentId, &clsid );
5268 encode_base85_guid( &clsid, component_85 );
5271 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5272 debugstr_w(component_85));
5274 sz = 20 + strlenW( feature ) + 20 + 3;
5275 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5276 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5277 return ret;
5280 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5282 MSIPACKAGE *package = param;
5283 LPCWSTR compgroupid, component, feature, qualifier, text;
5284 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5285 HKEY hkey = NULL;
5286 UINT rc;
5287 MSICOMPONENT *comp;
5288 MSIFEATURE *feat;
5289 DWORD sz;
5290 MSIRECORD *uirow;
5291 int len;
5293 feature = MSI_RecordGetString(rec, 5);
5294 feat = msi_get_loaded_feature(package, feature);
5295 if (!feat)
5296 return ERROR_SUCCESS;
5298 feat->Action = msi_get_feature_action( package, feat );
5299 if (feat->Action != INSTALLSTATE_LOCAL &&
5300 feat->Action != INSTALLSTATE_SOURCE &&
5301 feat->Action != INSTALLSTATE_ADVERTISED)
5303 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5304 return ERROR_SUCCESS;
5307 component = MSI_RecordGetString(rec, 3);
5308 comp = msi_get_loaded_component(package, component);
5309 if (!comp)
5310 return ERROR_SUCCESS;
5312 compgroupid = MSI_RecordGetString(rec,1);
5313 qualifier = MSI_RecordGetString(rec,2);
5315 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5316 if (rc != ERROR_SUCCESS)
5317 goto end;
5319 advertise = msi_create_component_advertise_string( package, comp, feature );
5320 text = MSI_RecordGetString( rec, 4 );
5321 if (text)
5323 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5324 strcpyW( p, advertise );
5325 strcatW( p, text );
5326 msi_free( advertise );
5327 advertise = p;
5329 existing = msi_reg_get_val_str( hkey, qualifier );
5331 sz = strlenW( advertise ) + 1;
5332 if (existing)
5334 for (p = existing; *p; p += len)
5336 len = strlenW( p ) + 1;
5337 if (strcmpW( advertise, p )) sz += len;
5340 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5342 rc = ERROR_OUTOFMEMORY;
5343 goto end;
5345 q = output;
5346 if (existing)
5348 for (p = existing; *p; p += len)
5350 len = strlenW( p ) + 1;
5351 if (strcmpW( advertise, p ))
5353 memcpy( q, p, len * sizeof(WCHAR) );
5354 q += len;
5358 strcpyW( q, advertise );
5359 q[strlenW( q ) + 1] = 0;
5361 msi_reg_set_val_multi_str( hkey, qualifier, output );
5363 end:
5364 RegCloseKey(hkey);
5365 msi_free( output );
5366 msi_free( advertise );
5367 msi_free( existing );
5369 /* the UI chunk */
5370 uirow = MSI_CreateRecord( 2 );
5371 MSI_RecordSetStringW( uirow, 1, compgroupid );
5372 MSI_RecordSetStringW( uirow, 2, qualifier);
5373 msi_ui_actiondata( package, szPublishComponents, uirow );
5374 msiobj_release( &uirow->hdr );
5375 /* FIXME: call ui_progress? */
5377 return rc;
5381 * At present I am ignorning the advertised components part of this and only
5382 * focusing on the qualified component sets
5384 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5386 static const WCHAR query[] = {
5387 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5388 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5389 MSIQUERY *view;
5390 UINT rc;
5392 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5393 if (rc != ERROR_SUCCESS)
5394 return ERROR_SUCCESS;
5396 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5397 msiobj_release(&view->hdr);
5398 return rc;
5401 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5403 static const WCHAR szInstallerComponents[] = {
5404 'S','o','f','t','w','a','r','e','\\',
5405 'M','i','c','r','o','s','o','f','t','\\',
5406 'I','n','s','t','a','l','l','e','r','\\',
5407 'C','o','m','p','o','n','e','n','t','s','\\',0};
5409 MSIPACKAGE *package = param;
5410 LPCWSTR compgroupid, component, feature, qualifier;
5411 MSICOMPONENT *comp;
5412 MSIFEATURE *feat;
5413 MSIRECORD *uirow;
5414 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5415 LONG res;
5417 feature = MSI_RecordGetString( rec, 5 );
5418 feat = msi_get_loaded_feature( package, feature );
5419 if (!feat)
5420 return ERROR_SUCCESS;
5422 feat->Action = msi_get_feature_action( package, feat );
5423 if (feat->Action != INSTALLSTATE_ABSENT)
5425 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5426 return ERROR_SUCCESS;
5429 component = MSI_RecordGetString( rec, 3 );
5430 comp = msi_get_loaded_component( package, component );
5431 if (!comp)
5432 return ERROR_SUCCESS;
5434 compgroupid = MSI_RecordGetString( rec, 1 );
5435 qualifier = MSI_RecordGetString( rec, 2 );
5437 squash_guid( compgroupid, squashed );
5438 strcpyW( keypath, szInstallerComponents );
5439 strcatW( keypath, squashed );
5441 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5442 if (res != ERROR_SUCCESS)
5444 WARN("Unable to delete component key %d\n", res);
5447 uirow = MSI_CreateRecord( 2 );
5448 MSI_RecordSetStringW( uirow, 1, compgroupid );
5449 MSI_RecordSetStringW( uirow, 2, qualifier );
5450 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5451 msiobj_release( &uirow->hdr );
5453 return ERROR_SUCCESS;
5456 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5458 static const WCHAR query[] = {
5459 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5460 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5461 MSIQUERY *view;
5462 UINT rc;
5464 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5465 if (rc != ERROR_SUCCESS)
5466 return ERROR_SUCCESS;
5468 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5469 msiobj_release( &view->hdr );
5470 return rc;
5473 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5475 static const WCHAR query[] =
5476 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5477 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5478 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5479 MSIPACKAGE *package = param;
5480 MSICOMPONENT *component;
5481 MSIRECORD *row;
5482 MSIFILE *file;
5483 SC_HANDLE hscm = NULL, service = NULL;
5484 LPCWSTR comp, key;
5485 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5486 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5487 DWORD serv_type, start_type, err_control;
5488 SERVICE_DESCRIPTIONW sd = {NULL};
5490 comp = MSI_RecordGetString( rec, 12 );
5491 component = msi_get_loaded_component( package, comp );
5492 if (!component)
5494 WARN("service component not found\n");
5495 goto done;
5497 component->Action = msi_get_component_action( package, component );
5498 if (component->Action != INSTALLSTATE_LOCAL)
5500 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5501 goto done;
5503 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5504 if (!hscm)
5506 ERR("Failed to open the SC Manager!\n");
5507 goto done;
5510 start_type = MSI_RecordGetInteger(rec, 5);
5511 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5512 goto done;
5514 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5515 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5516 serv_type = MSI_RecordGetInteger(rec, 4);
5517 err_control = MSI_RecordGetInteger(rec, 6);
5518 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5519 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5520 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5521 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5522 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5523 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5525 /* fetch the service path */
5526 row = MSI_QueryGetRecord(package->db, query, comp);
5527 if (!row)
5529 ERR("Query failed\n");
5530 goto done;
5532 key = MSI_RecordGetString(row, 6);
5533 file = msi_get_loaded_file(package, key);
5534 msiobj_release(&row->hdr);
5535 if (!file)
5537 ERR("Failed to load the service file\n");
5538 goto done;
5541 if (!args || !args[0]) image_path = file->TargetPath;
5542 else
5544 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5545 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5546 return ERROR_OUTOFMEMORY;
5548 strcpyW(image_path, file->TargetPath);
5549 strcatW(image_path, szSpace);
5550 strcatW(image_path, args);
5552 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5553 start_type, err_control, image_path, load_order,
5554 NULL, depends, serv_name, pass);
5556 if (!service)
5558 if (GetLastError() != ERROR_SERVICE_EXISTS)
5559 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5561 else if (sd.lpDescription)
5563 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5564 WARN("failed to set service description %u\n", GetLastError());
5567 if (image_path != file->TargetPath) msi_free(image_path);
5568 done:
5569 CloseServiceHandle(service);
5570 CloseServiceHandle(hscm);
5571 msi_free(name);
5572 msi_free(disp);
5573 msi_free(sd.lpDescription);
5574 msi_free(load_order);
5575 msi_free(serv_name);
5576 msi_free(pass);
5577 msi_free(depends);
5578 msi_free(args);
5580 return ERROR_SUCCESS;
5583 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5585 static const WCHAR query[] = {
5586 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5587 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5588 MSIQUERY *view;
5589 UINT rc;
5591 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5592 if (rc != ERROR_SUCCESS)
5593 return ERROR_SUCCESS;
5595 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5596 msiobj_release(&view->hdr);
5597 return rc;
5600 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5601 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5603 LPCWSTR *vector, *temp_vector;
5604 LPWSTR p, q;
5605 DWORD sep_len;
5607 static const WCHAR separator[] = {'[','~',']',0};
5609 *numargs = 0;
5610 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5612 if (!args)
5613 return NULL;
5615 vector = msi_alloc(sizeof(LPWSTR));
5616 if (!vector)
5617 return NULL;
5619 p = args;
5622 (*numargs)++;
5623 vector[*numargs - 1] = p;
5625 if ((q = strstrW(p, separator)))
5627 *q = '\0';
5629 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5630 if (!temp_vector)
5632 msi_free(vector);
5633 return NULL;
5635 vector = temp_vector;
5637 p = q + sep_len;
5639 } while (q);
5641 return vector;
5644 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5646 MSIPACKAGE *package = param;
5647 MSICOMPONENT *comp;
5648 MSIRECORD *uirow;
5649 SC_HANDLE scm = NULL, service = NULL;
5650 LPCWSTR component, *vector = NULL;
5651 LPWSTR name, args, display_name = NULL;
5652 DWORD event, numargs, len, wait, dummy;
5653 UINT r = ERROR_FUNCTION_FAILED;
5654 SERVICE_STATUS_PROCESS status;
5655 ULONGLONG start_time;
5657 component = MSI_RecordGetString(rec, 6);
5658 comp = msi_get_loaded_component(package, component);
5659 if (!comp)
5660 return ERROR_SUCCESS;
5662 comp->Action = msi_get_component_action( package, comp );
5663 if (comp->Action != INSTALLSTATE_LOCAL)
5665 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5666 return ERROR_SUCCESS;
5669 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5670 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5671 event = MSI_RecordGetInteger(rec, 3);
5672 wait = MSI_RecordGetInteger(rec, 5);
5674 if (!(event & msidbServiceControlEventStart))
5676 r = ERROR_SUCCESS;
5677 goto done;
5680 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5681 if (!scm)
5683 ERR("Failed to open the service control manager\n");
5684 goto done;
5687 len = 0;
5688 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5689 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5691 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5692 GetServiceDisplayNameW( scm, name, display_name, &len );
5695 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5696 if (!service)
5698 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5699 goto done;
5702 vector = msi_service_args_to_vector(args, &numargs);
5704 if (!StartServiceW(service, numargs, vector) &&
5705 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5707 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5708 goto done;
5711 r = ERROR_SUCCESS;
5712 if (wait)
5714 /* wait for at most 30 seconds for the service to be up and running */
5715 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5716 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5718 TRACE("failed to query service status (%u)\n", GetLastError());
5719 goto done;
5721 start_time = GetTickCount64();
5722 while (status.dwCurrentState == SERVICE_START_PENDING)
5724 if (GetTickCount64() - start_time > 30000) break;
5725 Sleep(1000);
5726 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5727 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5729 TRACE("failed to query service status (%u)\n", GetLastError());
5730 goto done;
5733 if (status.dwCurrentState != SERVICE_RUNNING)
5735 WARN("service failed to start %u\n", status.dwCurrentState);
5736 r = ERROR_FUNCTION_FAILED;
5740 done:
5741 uirow = MSI_CreateRecord( 2 );
5742 MSI_RecordSetStringW( uirow, 1, display_name );
5743 MSI_RecordSetStringW( uirow, 2, name );
5744 msi_ui_actiondata( package, szStartServices, uirow );
5745 msiobj_release( &uirow->hdr );
5747 CloseServiceHandle(service);
5748 CloseServiceHandle(scm);
5750 msi_free(name);
5751 msi_free(args);
5752 msi_free(vector);
5753 msi_free(display_name);
5754 return r;
5757 static UINT ACTION_StartServices( MSIPACKAGE *package )
5759 static const WCHAR query[] = {
5760 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5761 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5762 MSIQUERY *view;
5763 UINT rc;
5765 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5766 if (rc != ERROR_SUCCESS)
5767 return ERROR_SUCCESS;
5769 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5770 msiobj_release(&view->hdr);
5771 return rc;
5774 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5776 DWORD i, needed, count;
5777 ENUM_SERVICE_STATUSW *dependencies;
5778 SERVICE_STATUS ss;
5779 SC_HANDLE depserv;
5781 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5782 0, &needed, &count))
5783 return TRUE;
5785 if (GetLastError() != ERROR_MORE_DATA)
5786 return FALSE;
5788 dependencies = msi_alloc(needed);
5789 if (!dependencies)
5790 return FALSE;
5792 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5793 needed, &needed, &count))
5794 goto error;
5796 for (i = 0; i < count; i++)
5798 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5799 SERVICE_STOP | SERVICE_QUERY_STATUS);
5800 if (!depserv)
5801 goto error;
5803 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5804 goto error;
5807 return TRUE;
5809 error:
5810 msi_free(dependencies);
5811 return FALSE;
5814 static UINT stop_service( LPCWSTR name )
5816 SC_HANDLE scm = NULL, service = NULL;
5817 SERVICE_STATUS status;
5818 SERVICE_STATUS_PROCESS ssp;
5819 DWORD needed;
5821 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5822 if (!scm)
5824 WARN("Failed to open the SCM: %d\n", GetLastError());
5825 goto done;
5828 service = OpenServiceW(scm, name,
5829 SERVICE_STOP |
5830 SERVICE_QUERY_STATUS |
5831 SERVICE_ENUMERATE_DEPENDENTS);
5832 if (!service)
5834 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5835 goto done;
5838 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5839 sizeof(SERVICE_STATUS_PROCESS), &needed))
5841 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5842 goto done;
5845 if (ssp.dwCurrentState == SERVICE_STOPPED)
5846 goto done;
5848 stop_service_dependents(scm, service);
5850 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5851 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5853 done:
5854 CloseServiceHandle(service);
5855 CloseServiceHandle(scm);
5857 return ERROR_SUCCESS;
5860 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5862 MSIPACKAGE *package = param;
5863 MSICOMPONENT *comp;
5864 MSIRECORD *uirow;
5865 LPCWSTR component;
5866 LPWSTR name = NULL, display_name = NULL;
5867 DWORD event, len;
5868 SC_HANDLE scm;
5870 event = MSI_RecordGetInteger( rec, 3 );
5871 if (!(event & msidbServiceControlEventStop))
5872 return ERROR_SUCCESS;
5874 component = MSI_RecordGetString( rec, 6 );
5875 comp = msi_get_loaded_component( package, component );
5876 if (!comp)
5877 return ERROR_SUCCESS;
5879 comp->Action = msi_get_component_action( package, comp );
5880 if (comp->Action != INSTALLSTATE_ABSENT)
5882 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5883 return ERROR_SUCCESS;
5886 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5887 if (!scm)
5889 ERR("Failed to open the service control manager\n");
5890 goto done;
5893 len = 0;
5894 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5895 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5897 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5898 GetServiceDisplayNameW( scm, name, display_name, &len );
5900 CloseServiceHandle( scm );
5902 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5903 stop_service( name );
5905 done:
5906 uirow = MSI_CreateRecord( 2 );
5907 MSI_RecordSetStringW( uirow, 1, display_name );
5908 MSI_RecordSetStringW( uirow, 2, name );
5909 msi_ui_actiondata( package, szStopServices, uirow );
5910 msiobj_release( &uirow->hdr );
5912 msi_free( name );
5913 msi_free( display_name );
5914 return ERROR_SUCCESS;
5917 static UINT ACTION_StopServices( MSIPACKAGE *package )
5919 static const WCHAR query[] = {
5920 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5921 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5922 MSIQUERY *view;
5923 UINT rc;
5925 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5926 if (rc != ERROR_SUCCESS)
5927 return ERROR_SUCCESS;
5929 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5930 msiobj_release(&view->hdr);
5931 return rc;
5934 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5936 MSIPACKAGE *package = param;
5937 MSICOMPONENT *comp;
5938 MSIRECORD *uirow;
5939 LPCWSTR component;
5940 LPWSTR name = NULL, display_name = NULL;
5941 DWORD event, len;
5942 SC_HANDLE scm = NULL, service = NULL;
5944 event = MSI_RecordGetInteger( rec, 3 );
5945 if (!(event & msidbServiceControlEventDelete))
5946 return ERROR_SUCCESS;
5948 component = MSI_RecordGetString(rec, 6);
5949 comp = msi_get_loaded_component(package, component);
5950 if (!comp)
5951 return ERROR_SUCCESS;
5953 comp->Action = msi_get_component_action( package, comp );
5954 if (comp->Action != INSTALLSTATE_ABSENT)
5956 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5957 return ERROR_SUCCESS;
5960 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5961 stop_service( name );
5963 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5964 if (!scm)
5966 WARN("Failed to open the SCM: %d\n", GetLastError());
5967 goto done;
5970 len = 0;
5971 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5972 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5974 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5975 GetServiceDisplayNameW( scm, name, display_name, &len );
5978 service = OpenServiceW( scm, name, DELETE );
5979 if (!service)
5981 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5982 goto done;
5985 if (!DeleteService( service ))
5986 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5988 done:
5989 uirow = MSI_CreateRecord( 2 );
5990 MSI_RecordSetStringW( uirow, 1, display_name );
5991 MSI_RecordSetStringW( uirow, 2, name );
5992 msi_ui_actiondata( package, szDeleteServices, uirow );
5993 msiobj_release( &uirow->hdr );
5995 CloseServiceHandle( service );
5996 CloseServiceHandle( scm );
5997 msi_free( name );
5998 msi_free( display_name );
6000 return ERROR_SUCCESS;
6003 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6005 static const WCHAR query[] = {
6006 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6007 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6008 MSIQUERY *view;
6009 UINT rc;
6011 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6012 if (rc != ERROR_SUCCESS)
6013 return ERROR_SUCCESS;
6015 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6016 msiobj_release( &view->hdr );
6017 return rc;
6020 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6022 MSIPACKAGE *package = param;
6023 LPWSTR driver, driver_path, ptr;
6024 WCHAR outpath[MAX_PATH];
6025 MSIFILE *driver_file = NULL, *setup_file = NULL;
6026 MSICOMPONENT *comp;
6027 MSIRECORD *uirow;
6028 LPCWSTR desc, file_key, component;
6029 DWORD len, usage;
6030 UINT r = ERROR_SUCCESS;
6032 static const WCHAR driver_fmt[] = {
6033 'D','r','i','v','e','r','=','%','s',0};
6034 static const WCHAR setup_fmt[] = {
6035 'S','e','t','u','p','=','%','s',0};
6036 static const WCHAR usage_fmt[] = {
6037 'F','i','l','e','U','s','a','g','e','=','1',0};
6039 component = MSI_RecordGetString( rec, 2 );
6040 comp = msi_get_loaded_component( package, component );
6041 if (!comp)
6042 return ERROR_SUCCESS;
6044 comp->Action = msi_get_component_action( package, comp );
6045 if (comp->Action != INSTALLSTATE_LOCAL)
6047 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6048 return ERROR_SUCCESS;
6050 desc = MSI_RecordGetString(rec, 3);
6052 file_key = MSI_RecordGetString( rec, 4 );
6053 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6055 file_key = MSI_RecordGetString( rec, 5 );
6056 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6058 if (!driver_file)
6060 ERR("ODBC Driver entry not found!\n");
6061 return ERROR_FUNCTION_FAILED;
6064 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6065 if (setup_file)
6066 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6067 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6069 driver = msi_alloc(len * sizeof(WCHAR));
6070 if (!driver)
6071 return ERROR_OUTOFMEMORY;
6073 ptr = driver;
6074 lstrcpyW(ptr, desc);
6075 ptr += lstrlenW(ptr) + 1;
6077 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6078 ptr += len + 1;
6080 if (setup_file)
6082 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6083 ptr += len + 1;
6086 lstrcpyW(ptr, usage_fmt);
6087 ptr += lstrlenW(ptr) + 1;
6088 *ptr = '\0';
6090 driver_path = strdupW(driver_file->TargetPath);
6091 ptr = strrchrW(driver_path, '\\');
6092 if (ptr) *ptr = '\0';
6094 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6095 NULL, ODBC_INSTALL_COMPLETE, &usage))
6097 ERR("Failed to install SQL driver!\n");
6098 r = ERROR_FUNCTION_FAILED;
6101 uirow = MSI_CreateRecord( 5 );
6102 MSI_RecordSetStringW( uirow, 1, desc );
6103 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6104 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6105 msi_ui_actiondata( package, szInstallODBC, uirow );
6106 msiobj_release( &uirow->hdr );
6108 msi_free(driver);
6109 msi_free(driver_path);
6111 return r;
6114 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6116 MSIPACKAGE *package = param;
6117 LPWSTR translator, translator_path, ptr;
6118 WCHAR outpath[MAX_PATH];
6119 MSIFILE *translator_file = NULL, *setup_file = NULL;
6120 MSICOMPONENT *comp;
6121 MSIRECORD *uirow;
6122 LPCWSTR desc, file_key, component;
6123 DWORD len, usage;
6124 UINT r = ERROR_SUCCESS;
6126 static const WCHAR translator_fmt[] = {
6127 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6128 static const WCHAR setup_fmt[] = {
6129 'S','e','t','u','p','=','%','s',0};
6131 component = MSI_RecordGetString( rec, 2 );
6132 comp = msi_get_loaded_component( package, component );
6133 if (!comp)
6134 return ERROR_SUCCESS;
6136 comp->Action = msi_get_component_action( package, comp );
6137 if (comp->Action != INSTALLSTATE_LOCAL)
6139 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6140 return ERROR_SUCCESS;
6142 desc = MSI_RecordGetString(rec, 3);
6144 file_key = MSI_RecordGetString( rec, 4 );
6145 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6147 file_key = MSI_RecordGetString( rec, 5 );
6148 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6150 if (!translator_file)
6152 ERR("ODBC Translator entry not found!\n");
6153 return ERROR_FUNCTION_FAILED;
6156 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6157 if (setup_file)
6158 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6160 translator = msi_alloc(len * sizeof(WCHAR));
6161 if (!translator)
6162 return ERROR_OUTOFMEMORY;
6164 ptr = translator;
6165 lstrcpyW(ptr, desc);
6166 ptr += lstrlenW(ptr) + 1;
6168 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6169 ptr += len + 1;
6171 if (setup_file)
6173 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6174 ptr += len + 1;
6176 *ptr = '\0';
6178 translator_path = strdupW(translator_file->TargetPath);
6179 ptr = strrchrW(translator_path, '\\');
6180 if (ptr) *ptr = '\0';
6182 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6183 NULL, ODBC_INSTALL_COMPLETE, &usage))
6185 ERR("Failed to install SQL translator!\n");
6186 r = ERROR_FUNCTION_FAILED;
6189 uirow = MSI_CreateRecord( 5 );
6190 MSI_RecordSetStringW( uirow, 1, desc );
6191 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6192 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6193 msi_ui_actiondata( package, szInstallODBC, uirow );
6194 msiobj_release( &uirow->hdr );
6196 msi_free(translator);
6197 msi_free(translator_path);
6199 return r;
6202 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6204 MSIPACKAGE *package = param;
6205 MSICOMPONENT *comp;
6206 LPWSTR attrs;
6207 LPCWSTR desc, driver, component;
6208 WORD request = ODBC_ADD_SYS_DSN;
6209 INT registration;
6210 DWORD len;
6211 UINT r = ERROR_SUCCESS;
6212 MSIRECORD *uirow;
6214 static const WCHAR attrs_fmt[] = {
6215 'D','S','N','=','%','s',0 };
6217 component = MSI_RecordGetString( rec, 2 );
6218 comp = msi_get_loaded_component( package, component );
6219 if (!comp)
6220 return ERROR_SUCCESS;
6222 comp->Action = msi_get_component_action( package, comp );
6223 if (comp->Action != INSTALLSTATE_LOCAL)
6225 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6226 return ERROR_SUCCESS;
6229 desc = MSI_RecordGetString(rec, 3);
6230 driver = MSI_RecordGetString(rec, 4);
6231 registration = MSI_RecordGetInteger(rec, 5);
6233 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6234 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6236 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6237 attrs = msi_alloc(len * sizeof(WCHAR));
6238 if (!attrs)
6239 return ERROR_OUTOFMEMORY;
6241 len = sprintfW(attrs, attrs_fmt, desc);
6242 attrs[len + 1] = 0;
6244 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6246 ERR("Failed to install SQL data source!\n");
6247 r = ERROR_FUNCTION_FAILED;
6250 uirow = MSI_CreateRecord( 5 );
6251 MSI_RecordSetStringW( uirow, 1, desc );
6252 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6253 MSI_RecordSetInteger( uirow, 3, request );
6254 msi_ui_actiondata( package, szInstallODBC, uirow );
6255 msiobj_release( &uirow->hdr );
6257 msi_free(attrs);
6259 return r;
6262 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6264 static const WCHAR driver_query[] = {
6265 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6266 'O','D','B','C','D','r','i','v','e','r',0};
6267 static const WCHAR translator_query[] = {
6268 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6269 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6270 static const WCHAR source_query[] = {
6271 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6272 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6273 MSIQUERY *view;
6274 UINT rc;
6276 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6277 if (rc == ERROR_SUCCESS)
6279 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6280 msiobj_release(&view->hdr);
6281 if (rc != ERROR_SUCCESS)
6282 return rc;
6284 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6285 if (rc == ERROR_SUCCESS)
6287 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6288 msiobj_release(&view->hdr);
6289 if (rc != ERROR_SUCCESS)
6290 return rc;
6292 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6293 if (rc == ERROR_SUCCESS)
6295 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6296 msiobj_release(&view->hdr);
6297 if (rc != ERROR_SUCCESS)
6298 return rc;
6300 return ERROR_SUCCESS;
6303 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6305 MSIPACKAGE *package = param;
6306 MSICOMPONENT *comp;
6307 MSIRECORD *uirow;
6308 DWORD usage;
6309 LPCWSTR desc, component;
6311 component = MSI_RecordGetString( rec, 2 );
6312 comp = msi_get_loaded_component( package, component );
6313 if (!comp)
6314 return ERROR_SUCCESS;
6316 comp->Action = msi_get_component_action( package, comp );
6317 if (comp->Action != INSTALLSTATE_ABSENT)
6319 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6320 return ERROR_SUCCESS;
6323 desc = MSI_RecordGetString( rec, 3 );
6324 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6326 WARN("Failed to remove ODBC driver\n");
6328 else if (!usage)
6330 FIXME("Usage count reached 0\n");
6333 uirow = MSI_CreateRecord( 2 );
6334 MSI_RecordSetStringW( uirow, 1, desc );
6335 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6336 msi_ui_actiondata( package, szRemoveODBC, uirow );
6337 msiobj_release( &uirow->hdr );
6339 return ERROR_SUCCESS;
6342 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6344 MSIPACKAGE *package = param;
6345 MSICOMPONENT *comp;
6346 MSIRECORD *uirow;
6347 DWORD usage;
6348 LPCWSTR desc, component;
6350 component = MSI_RecordGetString( rec, 2 );
6351 comp = msi_get_loaded_component( package, component );
6352 if (!comp)
6353 return ERROR_SUCCESS;
6355 comp->Action = msi_get_component_action( package, comp );
6356 if (comp->Action != INSTALLSTATE_ABSENT)
6358 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6359 return ERROR_SUCCESS;
6362 desc = MSI_RecordGetString( rec, 3 );
6363 if (!SQLRemoveTranslatorW( desc, &usage ))
6365 WARN("Failed to remove ODBC translator\n");
6367 else if (!usage)
6369 FIXME("Usage count reached 0\n");
6372 uirow = MSI_CreateRecord( 2 );
6373 MSI_RecordSetStringW( uirow, 1, desc );
6374 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6375 msi_ui_actiondata( package, szRemoveODBC, uirow );
6376 msiobj_release( &uirow->hdr );
6378 return ERROR_SUCCESS;
6381 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6383 MSIPACKAGE *package = param;
6384 MSICOMPONENT *comp;
6385 MSIRECORD *uirow;
6386 LPWSTR attrs;
6387 LPCWSTR desc, driver, component;
6388 WORD request = ODBC_REMOVE_SYS_DSN;
6389 INT registration;
6390 DWORD len;
6392 static const WCHAR attrs_fmt[] = {
6393 'D','S','N','=','%','s',0 };
6395 component = MSI_RecordGetString( rec, 2 );
6396 comp = msi_get_loaded_component( package, component );
6397 if (!comp)
6398 return ERROR_SUCCESS;
6400 comp->Action = msi_get_component_action( package, comp );
6401 if (comp->Action != INSTALLSTATE_ABSENT)
6403 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6404 return ERROR_SUCCESS;
6407 desc = MSI_RecordGetString( rec, 3 );
6408 driver = MSI_RecordGetString( rec, 4 );
6409 registration = MSI_RecordGetInteger( rec, 5 );
6411 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6412 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6414 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6415 attrs = msi_alloc( len * sizeof(WCHAR) );
6416 if (!attrs)
6417 return ERROR_OUTOFMEMORY;
6419 FIXME("Use ODBCSourceAttribute table\n");
6421 len = sprintfW( attrs, attrs_fmt, desc );
6422 attrs[len + 1] = 0;
6424 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6426 WARN("Failed to remove ODBC data source\n");
6428 msi_free( attrs );
6430 uirow = MSI_CreateRecord( 3 );
6431 MSI_RecordSetStringW( uirow, 1, desc );
6432 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6433 MSI_RecordSetInteger( uirow, 3, request );
6434 msi_ui_actiondata( package, szRemoveODBC, uirow );
6435 msiobj_release( &uirow->hdr );
6437 return ERROR_SUCCESS;
6440 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6442 static const WCHAR driver_query[] = {
6443 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6444 'O','D','B','C','D','r','i','v','e','r',0};
6445 static const WCHAR translator_query[] = {
6446 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6447 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6448 static const WCHAR source_query[] = {
6449 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6450 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6451 MSIQUERY *view;
6452 UINT rc;
6454 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6455 if (rc == ERROR_SUCCESS)
6457 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6458 msiobj_release( &view->hdr );
6459 if (rc != ERROR_SUCCESS)
6460 return rc;
6462 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6463 if (rc == ERROR_SUCCESS)
6465 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6466 msiobj_release( &view->hdr );
6467 if (rc != ERROR_SUCCESS)
6468 return rc;
6470 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6471 if (rc == ERROR_SUCCESS)
6473 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6474 msiobj_release( &view->hdr );
6475 if (rc != ERROR_SUCCESS)
6476 return rc;
6478 return ERROR_SUCCESS;
6481 #define ENV_ACT_SETALWAYS 0x1
6482 #define ENV_ACT_SETABSENT 0x2
6483 #define ENV_ACT_REMOVE 0x4
6484 #define ENV_ACT_REMOVEMATCH 0x8
6486 #define ENV_MOD_MACHINE 0x20000000
6487 #define ENV_MOD_APPEND 0x40000000
6488 #define ENV_MOD_PREFIX 0x80000000
6489 #define ENV_MOD_MASK 0xC0000000
6491 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6493 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6495 LPCWSTR cptr = *name;
6497 static const WCHAR prefix[] = {'[','~',']',0};
6498 static const int prefix_len = 3;
6500 *flags = 0;
6501 while (*cptr)
6503 if (*cptr == '=')
6504 *flags |= ENV_ACT_SETALWAYS;
6505 else if (*cptr == '+')
6506 *flags |= ENV_ACT_SETABSENT;
6507 else if (*cptr == '-')
6508 *flags |= ENV_ACT_REMOVE;
6509 else if (*cptr == '!')
6510 *flags |= ENV_ACT_REMOVEMATCH;
6511 else if (*cptr == '*')
6512 *flags |= ENV_MOD_MACHINE;
6513 else
6514 break;
6516 cptr++;
6517 (*name)++;
6520 if (!*cptr)
6522 ERR("Missing environment variable\n");
6523 return ERROR_FUNCTION_FAILED;
6526 if (*value)
6528 LPCWSTR ptr = *value;
6529 if (!strncmpW(ptr, prefix, prefix_len))
6531 if (ptr[prefix_len] == szSemiColon[0])
6533 *flags |= ENV_MOD_APPEND;
6534 *value += lstrlenW(prefix);
6536 else
6538 *value = NULL;
6541 else if (lstrlenW(*value) >= prefix_len)
6543 ptr += lstrlenW(ptr) - prefix_len;
6544 if (!strcmpW( ptr, prefix ))
6546 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6548 *flags |= ENV_MOD_PREFIX;
6549 /* the "[~]" will be removed by deformat_string */;
6551 else
6553 *value = NULL;
6559 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6560 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6561 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6562 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6564 ERR("Invalid flags: %08x\n", *flags);
6565 return ERROR_FUNCTION_FAILED;
6568 if (!*flags)
6569 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6571 return ERROR_SUCCESS;
6574 static UINT open_env_key( DWORD flags, HKEY *key )
6576 static const WCHAR user_env[] =
6577 {'E','n','v','i','r','o','n','m','e','n','t',0};
6578 static const WCHAR machine_env[] =
6579 {'S','y','s','t','e','m','\\',
6580 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6581 'C','o','n','t','r','o','l','\\',
6582 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6583 'E','n','v','i','r','o','n','m','e','n','t',0};
6584 const WCHAR *env;
6585 HKEY root;
6586 LONG res;
6588 if (flags & ENV_MOD_MACHINE)
6590 env = machine_env;
6591 root = HKEY_LOCAL_MACHINE;
6593 else
6595 env = user_env;
6596 root = HKEY_CURRENT_USER;
6599 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6600 if (res != ERROR_SUCCESS)
6602 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6603 return ERROR_FUNCTION_FAILED;
6606 return ERROR_SUCCESS;
6609 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6611 MSIPACKAGE *package = param;
6612 LPCWSTR name, value, component;
6613 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6614 DWORD flags, type, size;
6615 UINT res;
6616 HKEY env = NULL;
6617 MSICOMPONENT *comp;
6618 MSIRECORD *uirow;
6619 int action = 0;
6621 component = MSI_RecordGetString(rec, 4);
6622 comp = msi_get_loaded_component(package, component);
6623 if (!comp)
6624 return ERROR_SUCCESS;
6626 comp->Action = msi_get_component_action( package, comp );
6627 if (comp->Action != INSTALLSTATE_LOCAL)
6629 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6630 return ERROR_SUCCESS;
6632 name = MSI_RecordGetString(rec, 2);
6633 value = MSI_RecordGetString(rec, 3);
6635 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6637 res = env_parse_flags(&name, &value, &flags);
6638 if (res != ERROR_SUCCESS || !value)
6639 goto done;
6641 if (value && !deformat_string(package, value, &deformatted))
6643 res = ERROR_OUTOFMEMORY;
6644 goto done;
6647 value = deformatted;
6649 res = open_env_key( flags, &env );
6650 if (res != ERROR_SUCCESS)
6651 goto done;
6653 if (flags & ENV_MOD_MACHINE)
6654 action |= 0x20000000;
6656 size = 0;
6657 type = REG_SZ;
6658 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6659 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6660 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6661 goto done;
6663 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6665 action = 0x2;
6667 /* Nothing to do. */
6668 if (!value)
6670 res = ERROR_SUCCESS;
6671 goto done;
6674 /* If we are appending but the string was empty, strip ; */
6675 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6677 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6678 newval = strdupW(value);
6679 if (!newval)
6681 res = ERROR_OUTOFMEMORY;
6682 goto done;
6685 else
6687 action = 0x1;
6689 /* Contrary to MSDN, +-variable to [~];path works */
6690 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6692 res = ERROR_SUCCESS;
6693 goto done;
6696 data = msi_alloc(size);
6697 if (!data)
6699 RegCloseKey(env);
6700 return ERROR_OUTOFMEMORY;
6703 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6704 if (res != ERROR_SUCCESS)
6705 goto done;
6707 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6709 action = 0x4;
6710 res = RegDeleteValueW(env, name);
6711 if (res != ERROR_SUCCESS)
6712 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6713 goto done;
6716 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6717 if (flags & ENV_MOD_MASK)
6719 DWORD mod_size;
6720 int multiplier = 0;
6721 if (flags & ENV_MOD_APPEND) multiplier++;
6722 if (flags & ENV_MOD_PREFIX) multiplier++;
6723 mod_size = lstrlenW(value) * multiplier;
6724 size += mod_size * sizeof(WCHAR);
6727 newval = msi_alloc(size);
6728 ptr = newval;
6729 if (!newval)
6731 res = ERROR_OUTOFMEMORY;
6732 goto done;
6735 if (flags & ENV_MOD_PREFIX)
6737 lstrcpyW(newval, value);
6738 ptr = newval + lstrlenW(value);
6739 action |= 0x80000000;
6742 lstrcpyW(ptr, data);
6744 if (flags & ENV_MOD_APPEND)
6746 lstrcatW(newval, value);
6747 action |= 0x40000000;
6750 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6751 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6752 if (res)
6754 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6757 done:
6758 uirow = MSI_CreateRecord( 3 );
6759 MSI_RecordSetStringW( uirow, 1, name );
6760 MSI_RecordSetStringW( uirow, 2, newval );
6761 MSI_RecordSetInteger( uirow, 3, action );
6762 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6763 msiobj_release( &uirow->hdr );
6765 if (env) RegCloseKey(env);
6766 msi_free(deformatted);
6767 msi_free(data);
6768 msi_free(newval);
6769 return res;
6772 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6774 static const WCHAR query[] = {
6775 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6776 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6777 MSIQUERY *view;
6778 UINT rc;
6780 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6781 if (rc != ERROR_SUCCESS)
6782 return ERROR_SUCCESS;
6784 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6785 msiobj_release(&view->hdr);
6786 return rc;
6789 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6791 MSIPACKAGE *package = param;
6792 LPCWSTR name, value, component;
6793 LPWSTR deformatted = NULL;
6794 DWORD flags;
6795 HKEY env;
6796 MSICOMPONENT *comp;
6797 MSIRECORD *uirow;
6798 int action = 0;
6799 LONG res;
6800 UINT r;
6802 component = MSI_RecordGetString( rec, 4 );
6803 comp = msi_get_loaded_component( package, component );
6804 if (!comp)
6805 return ERROR_SUCCESS;
6807 comp->Action = msi_get_component_action( package, comp );
6808 if (comp->Action != INSTALLSTATE_ABSENT)
6810 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6811 return ERROR_SUCCESS;
6813 name = MSI_RecordGetString( rec, 2 );
6814 value = MSI_RecordGetString( rec, 3 );
6816 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6818 r = env_parse_flags( &name, &value, &flags );
6819 if (r != ERROR_SUCCESS)
6820 return r;
6822 if (!(flags & ENV_ACT_REMOVE))
6824 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6825 return ERROR_SUCCESS;
6828 if (value && !deformat_string( package, value, &deformatted ))
6829 return ERROR_OUTOFMEMORY;
6831 value = deformatted;
6833 r = open_env_key( flags, &env );
6834 if (r != ERROR_SUCCESS)
6836 r = ERROR_SUCCESS;
6837 goto done;
6840 if (flags & ENV_MOD_MACHINE)
6841 action |= 0x20000000;
6843 TRACE("Removing %s\n", debugstr_w(name));
6845 res = RegDeleteValueW( env, name );
6846 if (res != ERROR_SUCCESS)
6848 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6849 r = ERROR_SUCCESS;
6852 done:
6853 uirow = MSI_CreateRecord( 3 );
6854 MSI_RecordSetStringW( uirow, 1, name );
6855 MSI_RecordSetStringW( uirow, 2, value );
6856 MSI_RecordSetInteger( uirow, 3, action );
6857 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6858 msiobj_release( &uirow->hdr );
6860 if (env) RegCloseKey( env );
6861 msi_free( deformatted );
6862 return r;
6865 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6867 static const WCHAR query[] = {
6868 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6869 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6870 MSIQUERY *view;
6871 UINT rc;
6873 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6874 if (rc != ERROR_SUCCESS)
6875 return ERROR_SUCCESS;
6877 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6878 msiobj_release( &view->hdr );
6879 return rc;
6882 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6884 LPWSTR key, template, id;
6885 UINT r = ERROR_SUCCESS;
6887 id = msi_dup_property( package->db, szProductID );
6888 if (id)
6890 msi_free( id );
6891 return ERROR_SUCCESS;
6893 template = msi_dup_property( package->db, szPIDTemplate );
6894 key = msi_dup_property( package->db, szPIDKEY );
6896 if (key && template)
6898 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6899 r = msi_set_property( package->db, szProductID, key );
6901 msi_free( template );
6902 msi_free( key );
6903 return r;
6906 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6908 TRACE("\n");
6909 package->need_reboot = 1;
6910 return ERROR_SUCCESS;
6913 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6915 static const WCHAR szAvailableFreeReg[] =
6916 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6917 MSIRECORD *uirow;
6918 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6920 TRACE("%p %d kilobytes\n", package, space);
6922 uirow = MSI_CreateRecord( 1 );
6923 MSI_RecordSetInteger( uirow, 1, space );
6924 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6925 msiobj_release( &uirow->hdr );
6927 return ERROR_SUCCESS;
6930 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6932 TRACE("%p\n", package);
6934 msi_set_property( package->db, szRollbackDisabled, szOne );
6935 return ERROR_SUCCESS;
6938 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6940 FIXME("%p\n", package);
6941 return ERROR_SUCCESS;
6944 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6946 static const WCHAR driver_query[] = {
6947 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6948 'O','D','B','C','D','r','i','v','e','r',0};
6949 static const WCHAR translator_query[] = {
6950 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6951 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6952 MSIQUERY *view;
6953 UINT r, count;
6955 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6956 if (r == ERROR_SUCCESS)
6958 count = 0;
6959 r = MSI_IterateRecords( view, &count, NULL, package );
6960 msiobj_release( &view->hdr );
6961 if (r != ERROR_SUCCESS)
6962 return r;
6963 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6965 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6966 if (r == ERROR_SUCCESS)
6968 count = 0;
6969 r = MSI_IterateRecords( view, &count, NULL, package );
6970 msiobj_release( &view->hdr );
6971 if (r != ERROR_SUCCESS)
6972 return r;
6973 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6975 return ERROR_SUCCESS;
6978 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6980 MSIPACKAGE *package = param;
6981 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6982 WCHAR *value;
6984 if ((value = msi_dup_property( package->db, property )))
6986 FIXME("remove %s\n", debugstr_w(value));
6987 msi_free( value );
6989 return ERROR_SUCCESS;
6992 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6994 static const WCHAR query[] = {
6995 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6996 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6997 MSIQUERY *view;
6998 UINT r;
7000 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7001 if (r == ERROR_SUCCESS)
7003 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7004 msiobj_release( &view->hdr );
7005 if (r != ERROR_SUCCESS)
7006 return r;
7008 return ERROR_SUCCESS;
7011 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7013 MSIPACKAGE *package = param;
7014 int attributes = MSI_RecordGetInteger( rec, 5 );
7016 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7018 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7019 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7020 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7021 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7022 HKEY hkey;
7023 UINT r;
7025 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7027 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7028 if (r != ERROR_SUCCESS)
7029 return ERROR_SUCCESS;
7031 else
7033 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7034 if (r != ERROR_SUCCESS)
7035 return ERROR_SUCCESS;
7037 RegCloseKey( hkey );
7039 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7040 debugstr_w(upgrade_code), debugstr_w(version_min),
7041 debugstr_w(version_max), debugstr_w(language));
7043 return ERROR_SUCCESS;
7046 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7048 static const WCHAR query[] = {
7049 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7050 'U','p','g','r','a','d','e',0};
7051 MSIQUERY *view;
7052 UINT r;
7054 if (msi_get_property_int( package->db, szInstalled, 0 ))
7056 TRACE("product is installed, skipping action\n");
7057 return ERROR_SUCCESS;
7059 if (msi_get_property_int( package->db, szPreselected, 0 ))
7061 TRACE("Preselected property is set, not migrating feature states\n");
7062 return ERROR_SUCCESS;
7064 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7065 if (r == ERROR_SUCCESS)
7067 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7068 msiobj_release( &view->hdr );
7069 if (r != ERROR_SUCCESS)
7070 return r;
7072 return ERROR_SUCCESS;
7075 static void bind_image( const char *filename, const char *path )
7077 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7079 WARN("failed to bind image %u\n", GetLastError());
7083 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7085 UINT i;
7086 MSIFILE *file;
7087 MSIPACKAGE *package = param;
7088 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7089 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7090 char *filenameA, *pathA;
7091 WCHAR *pathW, **path_list;
7093 if (!(file = msi_get_loaded_file( package, key )))
7095 WARN("file %s not found\n", debugstr_w(key));
7096 return ERROR_SUCCESS;
7098 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7099 path_list = msi_split_string( paths, ';' );
7100 if (!path_list) bind_image( filenameA, NULL );
7101 else
7103 for (i = 0; path_list[i] && path_list[i][0]; i++)
7105 deformat_string( package, path_list[i], &pathW );
7106 if ((pathA = strdupWtoA( pathW )))
7108 bind_image( filenameA, pathA );
7109 msi_free( pathA );
7111 msi_free( pathW );
7114 msi_free( path_list );
7115 msi_free( filenameA );
7116 return ERROR_SUCCESS;
7119 static UINT ACTION_BindImage( MSIPACKAGE *package )
7121 static const WCHAR query[] = {
7122 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7123 'B','i','n','d','I','m','a','g','e',0};
7124 MSIQUERY *view;
7125 UINT r;
7127 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7128 if (r == ERROR_SUCCESS)
7130 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7131 msiobj_release( &view->hdr );
7132 if (r != ERROR_SUCCESS)
7133 return r;
7135 return ERROR_SUCCESS;
7138 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7140 static const WCHAR query[] = {
7141 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7142 MSIQUERY *view;
7143 DWORD count = 0;
7144 UINT r;
7146 r = MSI_OpenQuery( package->db, &view, query, table );
7147 if (r == ERROR_SUCCESS)
7149 r = MSI_IterateRecords(view, &count, NULL, package);
7150 msiobj_release(&view->hdr);
7151 if (r != ERROR_SUCCESS)
7152 return r;
7154 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7155 return ERROR_SUCCESS;
7158 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7160 static const WCHAR table[] = {
7161 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7162 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7165 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7167 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7168 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7171 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7173 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7174 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7177 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7179 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7180 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7183 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7185 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7186 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7189 static const struct
7191 const WCHAR *action;
7192 UINT (*handler)(MSIPACKAGE *);
7193 const WCHAR *action_rollback;
7195 StandardActions[] =
7197 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7198 { szAppSearch, ACTION_AppSearch, NULL },
7199 { szBindImage, ACTION_BindImage, NULL },
7200 { szCCPSearch, ACTION_CCPSearch, NULL },
7201 { szCostFinalize, ACTION_CostFinalize, NULL },
7202 { szCostInitialize, ACTION_CostInitialize, NULL },
7203 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7204 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7205 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7206 { szDisableRollback, ACTION_DisableRollback, NULL },
7207 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7208 { szExecuteAction, ACTION_ExecuteAction, NULL },
7209 { szFileCost, ACTION_FileCost, NULL },
7210 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7211 { szForceReboot, ACTION_ForceReboot, NULL },
7212 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7213 { szInstallExecute, ACTION_InstallExecute, NULL },
7214 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7215 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7216 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7217 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7218 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7219 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7220 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7221 { szInstallValidate, ACTION_InstallValidate, NULL },
7222 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7223 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7224 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7225 { szMoveFiles, ACTION_MoveFiles, NULL },
7226 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7227 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7228 { szPatchFiles, ACTION_PatchFiles, NULL },
7229 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7230 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7231 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7232 { szPublishProduct, ACTION_PublishProduct, NULL },
7233 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7234 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7235 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7236 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7237 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7238 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7239 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7240 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7241 { szRegisterUser, ACTION_RegisterUser, NULL },
7242 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7243 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7244 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7245 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7246 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7247 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7248 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7249 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7250 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7251 { szResolveSource, ACTION_ResolveSource, NULL },
7252 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7253 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7254 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7255 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7256 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7257 { szStartServices, ACTION_StartServices, szStopServices },
7258 { szStopServices, ACTION_StopServices, szStartServices },
7259 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7260 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7261 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7262 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7263 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7264 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7265 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7266 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7267 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7268 { szValidateProductID, ACTION_ValidateProductID, NULL },
7269 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7270 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7271 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7272 { NULL, NULL, NULL }
7275 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7277 BOOL ret = FALSE;
7278 UINT i;
7280 i = 0;
7281 while (StandardActions[i].action != NULL)
7283 if (!strcmpW( StandardActions[i].action, action ))
7285 ui_actionstart( package, action );
7286 if (StandardActions[i].handler)
7288 ui_actioninfo( package, action, TRUE, 0 );
7289 *rc = StandardActions[i].handler( package );
7290 ui_actioninfo( package, action, FALSE, *rc );
7292 if (StandardActions[i].action_rollback && !package->need_rollback)
7294 TRACE("scheduling rollback action\n");
7295 msi_schedule_action( package, ROLLBACK_SCRIPT, StandardActions[i].action_rollback );
7298 else
7300 FIXME("unhandled standard action %s\n", debugstr_w(action));
7301 *rc = ERROR_SUCCESS;
7303 ret = TRUE;
7304 break;
7306 i++;
7308 return ret;
7311 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7313 UINT rc = ERROR_SUCCESS;
7314 BOOL handled;
7316 TRACE("Performing action (%s)\n", debugstr_w(action));
7318 handled = ACTION_HandleStandardAction(package, action, &rc);
7320 if (!handled)
7321 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7323 if (!handled)
7325 WARN("unhandled msi action %s\n", debugstr_w(action));
7326 rc = ERROR_FUNCTION_NOT_CALLED;
7329 return rc;
7332 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7334 UINT rc = ERROR_SUCCESS;
7335 BOOL handled = FALSE;
7337 TRACE("Performing action (%s)\n", debugstr_w(action));
7339 handled = ACTION_HandleStandardAction(package, action, &rc);
7341 if (!handled)
7342 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7344 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7345 handled = TRUE;
7347 if (!handled)
7349 WARN("unhandled msi action %s\n", debugstr_w(action));
7350 rc = ERROR_FUNCTION_NOT_CALLED;
7353 return rc;
7356 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7358 UINT rc = ERROR_SUCCESS;
7359 MSIRECORD *row;
7361 static const WCHAR query[] =
7362 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7363 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7364 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7365 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7366 static const WCHAR ui_query[] =
7367 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7368 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7369 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7370 ' ', '=',' ','%','i',0};
7372 if (needs_ui_sequence(package))
7373 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7374 else
7375 row = MSI_QueryGetRecord(package->db, query, seq);
7377 if (row)
7379 LPCWSTR action, cond;
7381 TRACE("Running the actions\n");
7383 /* check conditions */
7384 cond = MSI_RecordGetString(row, 2);
7386 /* this is a hack to skip errors in the condition code */
7387 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7389 msiobj_release(&row->hdr);
7390 return ERROR_SUCCESS;
7393 action = MSI_RecordGetString(row, 1);
7394 if (!action)
7396 ERR("failed to fetch action\n");
7397 msiobj_release(&row->hdr);
7398 return ERROR_FUNCTION_FAILED;
7401 if (needs_ui_sequence(package))
7402 rc = ACTION_PerformUIAction(package, action, -1);
7403 else
7404 rc = ACTION_PerformAction(package, action, -1);
7406 msiobj_release(&row->hdr);
7409 return rc;
7412 /****************************************************
7413 * TOP level entry points
7414 *****************************************************/
7416 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7417 LPCWSTR szCommandLine )
7419 UINT rc;
7420 BOOL ui_exists;
7421 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7422 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7423 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7425 msi_set_property( package->db, szAction, szInstall );
7427 package->script->InWhatSequence = SEQUENCE_INSTALL;
7429 if (szPackagePath)
7431 LPWSTR p, dir;
7432 LPCWSTR file;
7434 dir = strdupW(szPackagePath);
7435 p = strrchrW(dir, '\\');
7436 if (p)
7438 *(++p) = 0;
7439 file = szPackagePath + (p - dir);
7441 else
7443 msi_free(dir);
7444 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7445 GetCurrentDirectoryW(MAX_PATH, dir);
7446 lstrcatW(dir, szBackSlash);
7447 file = szPackagePath;
7450 msi_free( package->PackagePath );
7451 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7452 if (!package->PackagePath)
7454 msi_free(dir);
7455 return ERROR_OUTOFMEMORY;
7458 lstrcpyW(package->PackagePath, dir);
7459 lstrcatW(package->PackagePath, file);
7460 msi_free(dir);
7462 msi_set_sourcedir_props(package, FALSE);
7465 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7466 if (rc != ERROR_SUCCESS)
7467 return rc;
7469 msi_apply_transforms( package );
7470 msi_apply_patches( package );
7472 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7474 TRACE("setting reinstall property\n");
7475 msi_set_property( package->db, szReinstall, szAll );
7478 /* properties may have been added by a transform */
7479 msi_clone_properties( package );
7481 msi_parse_command_line( package, szCommandLine, FALSE );
7482 msi_adjust_privilege_properties( package );
7483 msi_set_context( package );
7485 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7487 TRACE("disabling rollback\n");
7488 msi_set_property( package->db, szRollbackDisabled, szOne );
7491 if (needs_ui_sequence( package))
7493 package->script->InWhatSequence |= SEQUENCE_UI;
7494 rc = ACTION_ProcessUISequence(package);
7495 ui_exists = ui_sequence_exists(package);
7496 if (rc == ERROR_SUCCESS || !ui_exists)
7498 package->script->InWhatSequence |= SEQUENCE_EXEC;
7499 rc = ACTION_ProcessExecSequence(package, ui_exists);
7502 else
7503 rc = ACTION_ProcessExecSequence(package, FALSE);
7505 package->script->CurrentlyScripting = FALSE;
7507 /* process the ending type action */
7508 if (rc == ERROR_SUCCESS)
7509 ACTION_PerformActionSequence(package, -1);
7510 else if (rc == ERROR_INSTALL_USEREXIT)
7511 ACTION_PerformActionSequence(package, -2);
7512 else if (rc == ERROR_INSTALL_SUSPEND)
7513 ACTION_PerformActionSequence(package, -4);
7514 else /* failed */
7516 ACTION_PerformActionSequence(package, -3);
7517 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7519 package->need_rollback = TRUE;
7523 /* finish up running custom actions */
7524 ACTION_FinishCustomActions(package);
7526 if (package->need_rollback)
7528 WARN("installation failed, running rollback script\n");
7529 execute_script( package, ROLLBACK_SCRIPT );
7532 if (rc == ERROR_SUCCESS && package->need_reboot)
7533 return ERROR_SUCCESS_REBOOT_REQUIRED;
7535 return rc;