msi: Execute custom actions in the right script.
[wine/multimedia.git] / dlls / msi / action.c
blob5444dfc05aff08285a4e5be5c1c2bf1ee74256cd
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 if (!count) goto done;
226 in_quotes = 1;
227 ignore = 1;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes && p[1] != '\"') count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 if (!count) in_quotes = 0;
238 else in_quotes = 1;
239 len++;
240 break;
242 break;
244 case state_token:
245 switch (*p)
247 case '"':
248 state = state_quote;
249 if (in_quotes) count--;
250 else count++;
251 break;
252 case ' ':
253 state = state_whitespace;
254 if (!count) goto done;
255 in_quotes = 1;
256 len++;
257 break;
258 default:
259 if (!count) in_quotes = 0;
260 else in_quotes = 1;
261 len++;
262 break;
264 break;
266 case state_quote:
267 switch (*p)
269 case '"':
270 if (in_quotes && p[1] != '\"') count--;
271 else count++;
272 break;
273 case ' ':
274 state = state_whitespace;
275 if (!count || (count > 1 && !len)) goto done;
276 in_quotes = 1;
277 len++;
278 break;
279 default:
280 state = state_token;
281 if (!count) in_quotes = 0;
282 else in_quotes = 1;
283 len++;
284 break;
286 break;
288 default: break;
290 if (!ignore) *out++ = *p;
293 done:
294 if (!len) *value = 0;
295 else *out = 0;
297 *quotes = count;
298 return p - str;
301 static void remove_quotes( WCHAR *str )
303 WCHAR *p = str;
304 int len = strlenW( str );
306 while ((p = strchrW( p, '"' )))
308 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
309 p++;
313 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
314 BOOL preserve_case )
316 LPCWSTR ptr, ptr2;
317 int num_quotes;
318 DWORD len;
319 WCHAR *prop, *val;
320 UINT r;
322 if (!szCommandLine)
323 return ERROR_SUCCESS;
325 ptr = szCommandLine;
326 while (*ptr)
328 while (*ptr == ' ') ptr++;
329 if (!*ptr) break;
331 ptr2 = strchrW( ptr, '=' );
332 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
334 len = ptr2 - ptr;
335 if (!len) return ERROR_INVALID_COMMAND_LINE;
337 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
338 memcpy( prop, ptr, len * sizeof(WCHAR) );
339 prop[len] = 0;
340 if (!preserve_case) struprW( prop );
342 ptr2++;
343 while (*ptr2 == ' ') ptr2++;
345 num_quotes = 0;
346 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
347 len = parse_prop( ptr2, val, &num_quotes );
348 if (num_quotes % 2)
350 WARN("unbalanced quotes\n");
351 msi_free( val );
352 msi_free( prop );
353 return ERROR_INVALID_COMMAND_LINE;
355 remove_quotes( val );
356 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
358 r = msi_set_property( package->db, prop, val );
359 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
360 msi_reset_folders( package, TRUE );
362 msi_free( val );
363 msi_free( prop );
365 ptr = ptr2 + len;
368 return ERROR_SUCCESS;
371 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
373 LPCWSTR pc;
374 LPWSTR p, *ret = NULL;
375 UINT count = 0;
377 if (!str)
378 return ret;
380 /* count the number of substrings */
381 for ( pc = str, count = 0; pc; count++ )
383 pc = strchrW( pc, sep );
384 if (pc)
385 pc++;
388 /* allocate space for an array of substring pointers and the substrings */
389 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
390 (lstrlenW(str)+1) * sizeof(WCHAR) );
391 if (!ret)
392 return ret;
394 /* copy the string and set the pointers */
395 p = (LPWSTR) &ret[count+1];
396 lstrcpyW( p, str );
397 for( count = 0; (ret[count] = p); count++ )
399 p = strchrW( p, sep );
400 if (p)
401 *p++ = 0;
404 return ret;
407 static BOOL ui_sequence_exists( MSIPACKAGE *package )
409 static const WCHAR query [] = {
410 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
411 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
412 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
413 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
414 MSIQUERY *view;
415 UINT rc;
417 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
418 if (rc == ERROR_SUCCESS)
420 msiobj_release(&view->hdr);
421 return TRUE;
423 return FALSE;
426 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
428 LPWSTR source, check;
430 if (msi_get_property_int( package->db, szInstalled, 0 ))
432 HKEY hkey;
434 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
435 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
436 RegCloseKey( hkey );
438 else
440 LPWSTR p, db;
441 DWORD len;
443 db = msi_dup_property( package->db, szOriginalDatabase );
444 if (!db)
445 return ERROR_OUTOFMEMORY;
447 p = strrchrW( db, '\\' );
448 if (!p)
450 p = strrchrW( db, '/' );
451 if (!p)
453 msi_free(db);
454 return ERROR_SUCCESS;
458 len = p - db + 2;
459 source = msi_alloc( len * sizeof(WCHAR) );
460 lstrcpynW( source, db, len );
461 msi_free( db );
464 check = msi_dup_property( package->db, szSourceDir );
465 if (!check || replace)
467 UINT r = msi_set_property( package->db, szSourceDir, source );
468 if (r == ERROR_SUCCESS)
469 msi_reset_folders( package, TRUE );
471 msi_free( check );
473 check = msi_dup_property( package->db, szSOURCEDIR );
474 if (!check || replace)
475 msi_set_property( package->db, szSOURCEDIR, source );
477 msi_free( check );
478 msi_free( source );
480 return ERROR_SUCCESS;
483 static BOOL needs_ui_sequence(MSIPACKAGE *package)
485 INT level = msi_get_property_int(package->db, szUILevel, 0);
486 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
489 UINT msi_set_context(MSIPACKAGE *package)
491 UINT r = msi_locate_product( package->ProductCode, &package->Context );
492 if (r != ERROR_SUCCESS)
494 int num = msi_get_property_int( package->db, szAllUsers, 0 );
495 if (num == 1 || num == 2)
496 package->Context = MSIINSTALLCONTEXT_MACHINE;
497 else
498 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
500 return ERROR_SUCCESS;
503 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
505 UINT rc;
506 LPCWSTR cond, action;
507 MSIPACKAGE *package = param;
509 action = MSI_RecordGetString(row,1);
510 if (!action)
512 ERR("Error is retrieving action name\n");
513 return ERROR_FUNCTION_FAILED;
516 /* check conditions */
517 cond = MSI_RecordGetString(row,2);
519 /* this is a hack to skip errors in the condition code */
520 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
522 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
523 return ERROR_SUCCESS;
526 if (needs_ui_sequence(package))
527 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
528 else
529 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
531 msi_dialog_check_messages( NULL );
533 if (package->CurrentInstallState != ERROR_SUCCESS)
534 rc = package->CurrentInstallState;
536 if (rc == ERROR_FUNCTION_NOT_CALLED)
537 rc = ERROR_SUCCESS;
539 if (rc != ERROR_SUCCESS)
540 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
542 return rc;
545 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
547 static const WCHAR query[] = {
548 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
549 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
550 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
551 '`','S','e','q','u','e','n','c','e','`',0};
552 MSIQUERY *view;
553 UINT r;
555 TRACE("%p %s\n", package, debugstr_w(table));
557 r = MSI_OpenQuery( package->db, &view, query, table );
558 if (r == ERROR_SUCCESS)
560 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
561 msiobj_release(&view->hdr);
563 return r;
566 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
568 static const WCHAR query[] = {
569 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
570 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
571 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
572 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
573 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
574 static const WCHAR query_validate[] = {
575 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
576 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
577 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
578 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
579 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
580 MSIQUERY *view;
581 INT seq = 0;
582 UINT rc;
584 if (package->script->ExecuteSequenceRun)
586 TRACE("Execute Sequence already Run\n");
587 return ERROR_SUCCESS;
590 package->script->ExecuteSequenceRun = TRUE;
592 /* get the sequence number */
593 if (UIran)
595 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
596 if (!row) return ERROR_FUNCTION_FAILED;
597 seq = MSI_RecordGetInteger(row,1);
598 msiobj_release(&row->hdr);
600 rc = MSI_OpenQuery(package->db, &view, query, seq);
601 if (rc == ERROR_SUCCESS)
603 TRACE("Running the actions\n");
605 msi_set_property(package->db, szSourceDir, NULL);
606 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
607 msiobj_release(&view->hdr);
609 return rc;
612 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
614 static const WCHAR query[] = {
615 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
616 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
617 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
618 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
619 MSIQUERY *view;
620 UINT rc;
622 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
623 if (rc == ERROR_SUCCESS)
625 TRACE("Running the actions\n");
626 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
627 msiobj_release(&view->hdr);
629 return rc;
632 /********************************************************
633 * ACTION helper functions and functions that perform the actions
634 *******************************************************/
635 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
636 UINT* rc, UINT script, BOOL force )
638 BOOL ret=FALSE;
639 UINT arc;
641 arc = ACTION_CustomAction(package, action, script, force);
643 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
645 *rc = arc;
646 ret = TRUE;
648 return ret;
651 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
653 MSICOMPONENT *comp;
655 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
657 if (!strcmpW( Component, comp->Component )) return comp;
659 return NULL;
662 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
664 MSIFEATURE *feature;
666 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
668 if (!strcmpW( Feature, feature->Feature )) return feature;
670 return NULL;
673 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
675 MSIFILE *file;
677 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
679 if (!strcmpW( key, file->File )) return file;
681 return NULL;
684 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
686 MSIFILEPATCH *patch;
688 /* FIXME: There might be more than one patch */
689 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
691 if (!strcmpW( key, patch->File->File )) return patch;
693 return NULL;
696 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
698 MSIFOLDER *folder;
700 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
702 if (!strcmpW( dir, folder->Directory )) return folder;
704 return NULL;
708 * Recursively create all directories in the path.
709 * shamelessly stolen from setupapi/queue.c
711 BOOL msi_create_full_path( const WCHAR *path )
713 BOOL ret = TRUE;
714 WCHAR *new_path;
715 int len;
717 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
718 strcpyW( new_path, path );
720 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
721 new_path[len - 1] = 0;
723 while (!CreateDirectoryW( new_path, NULL ))
725 WCHAR *slash;
726 DWORD last_error = GetLastError();
727 if (last_error == ERROR_ALREADY_EXISTS) break;
728 if (last_error != ERROR_PATH_NOT_FOUND)
730 ret = FALSE;
731 break;
733 if (!(slash = strrchrW( new_path, '\\' )))
735 ret = FALSE;
736 break;
738 len = slash - new_path;
739 new_path[len] = 0;
740 if (!msi_create_full_path( new_path ))
742 ret = FALSE;
743 break;
745 new_path[len] = '\\';
747 msi_free( new_path );
748 return ret;
751 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
753 MSIRECORD *row;
755 row = MSI_CreateRecord( 4 );
756 MSI_RecordSetInteger( row, 1, a );
757 MSI_RecordSetInteger( row, 2, b );
758 MSI_RecordSetInteger( row, 3, c );
759 MSI_RecordSetInteger( row, 4, d );
760 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
761 msiobj_release( &row->hdr );
763 msi_dialog_check_messages( NULL );
766 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
768 static const WCHAR query[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
770 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
771 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
772 WCHAR message[1024];
773 MSIRECORD *row = 0;
774 DWORD size;
776 if (!package->LastAction || strcmpW( package->LastAction, action ))
778 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
780 if (MSI_RecordIsNull( row, 3 ))
782 msiobj_release( &row->hdr );
783 return;
785 /* update the cached action format */
786 msi_free( package->ActionFormat );
787 package->ActionFormat = msi_dup_record_field( row, 3 );
788 msi_free( package->LastAction );
789 package->LastAction = strdupW( action );
790 msiobj_release( &row->hdr );
792 size = 1024;
793 MSI_RecordSetStringW( record, 0, package->ActionFormat );
794 MSI_FormatRecordW( package, record, message, &size );
795 row = MSI_CreateRecord( 1 );
796 MSI_RecordSetStringW( row, 1, message );
797 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
798 msiobj_release( &row->hdr );
801 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
803 if (!comp->Enabled)
805 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
806 return INSTALLSTATE_UNKNOWN;
808 if (package->need_rollback) return comp->Installed;
809 return comp->ActionRequest;
812 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
814 if (package->need_rollback) return feature->Installed;
815 return feature->ActionRequest;
818 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
820 MSIPACKAGE *package = param;
821 LPCWSTR dir, component, full_path;
822 MSIRECORD *uirow;
823 MSIFOLDER *folder;
824 MSICOMPONENT *comp;
826 component = MSI_RecordGetString(row, 2);
827 if (!component)
828 return ERROR_SUCCESS;
830 comp = msi_get_loaded_component(package, component);
831 if (!comp)
832 return ERROR_SUCCESS;
834 comp->Action = msi_get_component_action( package, comp );
835 if (comp->Action != INSTALLSTATE_LOCAL)
837 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
838 return ERROR_SUCCESS;
841 dir = MSI_RecordGetString(row,1);
842 if (!dir)
844 ERR("Unable to get folder id\n");
845 return ERROR_SUCCESS;
848 uirow = MSI_CreateRecord(1);
849 MSI_RecordSetStringW(uirow, 1, dir);
850 msi_ui_actiondata(package, szCreateFolders, uirow);
851 msiobj_release(&uirow->hdr);
853 full_path = msi_get_target_folder( package, dir );
854 if (!full_path)
856 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
857 return ERROR_SUCCESS;
859 TRACE("folder is %s\n", debugstr_w(full_path));
861 folder = msi_get_loaded_folder( package, dir );
862 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
863 folder->State = FOLDER_STATE_CREATED;
864 return ERROR_SUCCESS;
867 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
869 static const WCHAR query[] = {
870 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
871 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
872 MSIQUERY *view;
873 UINT rc;
875 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
876 if (rc != ERROR_SUCCESS)
877 return ERROR_SUCCESS;
879 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
880 msiobj_release(&view->hdr);
881 return rc;
884 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
886 MSIPACKAGE *package = param;
887 LPCWSTR dir, component, full_path;
888 MSIRECORD *uirow;
889 MSIFOLDER *folder;
890 MSICOMPONENT *comp;
892 component = MSI_RecordGetString(row, 2);
893 if (!component)
894 return ERROR_SUCCESS;
896 comp = msi_get_loaded_component(package, component);
897 if (!comp)
898 return ERROR_SUCCESS;
900 comp->Action = msi_get_component_action( package, comp );
901 if (comp->Action != INSTALLSTATE_ABSENT)
903 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
904 return ERROR_SUCCESS;
907 dir = MSI_RecordGetString( row, 1 );
908 if (!dir)
910 ERR("Unable to get folder id\n");
911 return ERROR_SUCCESS;
914 full_path = msi_get_target_folder( package, dir );
915 if (!full_path)
917 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
918 return ERROR_SUCCESS;
920 TRACE("folder is %s\n", debugstr_w(full_path));
922 uirow = MSI_CreateRecord( 1 );
923 MSI_RecordSetStringW( uirow, 1, dir );
924 msi_ui_actiondata( package, szRemoveFolders, uirow );
925 msiobj_release( &uirow->hdr );
927 RemoveDirectoryW( full_path );
928 folder = msi_get_loaded_folder( package, dir );
929 folder->State = FOLDER_STATE_REMOVED;
930 return ERROR_SUCCESS;
933 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
935 static const WCHAR query[] = {
936 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
937 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
938 MSIQUERY *view;
939 UINT rc;
941 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
942 if (rc != ERROR_SUCCESS)
943 return ERROR_SUCCESS;
945 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
946 msiobj_release( &view->hdr );
947 return rc;
950 static UINT load_component( MSIRECORD *row, LPVOID param )
952 MSIPACKAGE *package = param;
953 MSICOMPONENT *comp;
955 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
956 if (!comp)
957 return ERROR_FUNCTION_FAILED;
959 list_add_tail( &package->components, &comp->entry );
961 /* fill in the data */
962 comp->Component = msi_dup_record_field( row, 1 );
964 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
966 comp->ComponentId = msi_dup_record_field( row, 2 );
967 comp->Directory = msi_dup_record_field( row, 3 );
968 comp->Attributes = MSI_RecordGetInteger(row,4);
969 comp->Condition = msi_dup_record_field( row, 5 );
970 comp->KeyPath = msi_dup_record_field( row, 6 );
972 comp->Installed = INSTALLSTATE_UNKNOWN;
973 comp->Action = INSTALLSTATE_UNKNOWN;
974 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
976 comp->assembly = msi_load_assembly( package, comp );
977 return ERROR_SUCCESS;
980 UINT msi_load_all_components( MSIPACKAGE *package )
982 static const WCHAR query[] = {
983 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
984 '`','C','o','m','p','o','n','e','n','t','`',0};
985 MSIQUERY *view;
986 UINT r;
988 if (!list_empty(&package->components))
989 return ERROR_SUCCESS;
991 r = MSI_DatabaseOpenViewW( package->db, query, &view );
992 if (r != ERROR_SUCCESS)
993 return r;
995 if (!msi_init_assembly_caches( package ))
997 ERR("can't initialize assembly caches\n");
998 msiobj_release( &view->hdr );
999 return ERROR_FUNCTION_FAILED;
1002 r = MSI_IterateRecords(view, NULL, load_component, package);
1003 msiobj_release(&view->hdr);
1004 msi_destroy_assembly_caches( package );
1005 return r;
1008 typedef struct {
1009 MSIPACKAGE *package;
1010 MSIFEATURE *feature;
1011 } _ilfs;
1013 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1015 ComponentList *cl;
1017 cl = msi_alloc( sizeof (*cl) );
1018 if ( !cl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 cl->component = comp;
1021 list_add_tail( &feature->Components, &cl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1028 FeatureList *fl;
1030 fl = msi_alloc( sizeof(*fl) );
1031 if ( !fl )
1032 return ERROR_NOT_ENOUGH_MEMORY;
1033 fl->feature = child;
1034 list_add_tail( &parent->Children, &fl->entry );
1036 return ERROR_SUCCESS;
1039 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1041 _ilfs* ilfs = param;
1042 LPCWSTR component;
1043 MSICOMPONENT *comp;
1045 component = MSI_RecordGetString(row,1);
1047 /* check to see if the component is already loaded */
1048 comp = msi_get_loaded_component( ilfs->package, component );
1049 if (!comp)
1051 WARN("ignoring unknown component %s\n", debugstr_w(component));
1052 return ERROR_SUCCESS;
1054 add_feature_component( ilfs->feature, comp );
1055 comp->Enabled = TRUE;
1057 return ERROR_SUCCESS;
1060 static UINT load_feature(MSIRECORD * row, LPVOID param)
1062 static const WCHAR query[] = {
1063 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1064 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1065 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1066 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1067 MSIPACKAGE *package = param;
1068 MSIFEATURE *feature;
1069 MSIQUERY *view;
1070 _ilfs ilfs;
1071 UINT rc;
1073 /* fill in the data */
1075 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1076 if (!feature)
1077 return ERROR_NOT_ENOUGH_MEMORY;
1079 list_init( &feature->Children );
1080 list_init( &feature->Components );
1082 feature->Feature = msi_dup_record_field( row, 1 );
1084 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1086 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1087 feature->Title = msi_dup_record_field( row, 3 );
1088 feature->Description = msi_dup_record_field( row, 4 );
1090 if (!MSI_RecordIsNull(row,5))
1091 feature->Display = MSI_RecordGetInteger(row,5);
1093 feature->Level= MSI_RecordGetInteger(row,6);
1094 feature->Directory = msi_dup_record_field( row, 7 );
1095 feature->Attributes = MSI_RecordGetInteger(row,8);
1097 feature->Installed = INSTALLSTATE_UNKNOWN;
1098 feature->Action = INSTALLSTATE_UNKNOWN;
1099 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1101 list_add_tail( &package->features, &feature->entry );
1103 /* load feature components */
1105 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1106 if (rc != ERROR_SUCCESS)
1107 return ERROR_SUCCESS;
1109 ilfs.package = package;
1110 ilfs.feature = feature;
1112 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1113 msiobj_release(&view->hdr);
1114 return rc;
1117 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1119 MSIPACKAGE *package = param;
1120 MSIFEATURE *parent, *child;
1122 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1123 if (!child)
1124 return ERROR_FUNCTION_FAILED;
1126 if (!child->Feature_Parent)
1127 return ERROR_SUCCESS;
1129 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1130 if (!parent)
1131 return ERROR_FUNCTION_FAILED;
1133 add_feature_child( parent, child );
1134 return ERROR_SUCCESS;
1137 UINT msi_load_all_features( MSIPACKAGE *package )
1139 static const WCHAR query[] = {
1140 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1141 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1142 '`','D','i','s','p','l','a','y','`',0};
1143 MSIQUERY *view;
1144 UINT r;
1146 if (!list_empty(&package->features))
1147 return ERROR_SUCCESS;
1149 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1150 if (r != ERROR_SUCCESS)
1151 return r;
1153 r = MSI_IterateRecords( view, NULL, load_feature, package );
1154 if (r != ERROR_SUCCESS)
1156 msiobj_release( &view->hdr );
1157 return r;
1159 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1160 msiobj_release( &view->hdr );
1161 return r;
1164 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1166 if (!p)
1167 return p;
1168 p = strchrW(p, ch);
1169 if (!p)
1170 return p;
1171 *p = 0;
1172 return p+1;
1175 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1177 static const WCHAR query[] = {
1178 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1179 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1180 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1181 MSIQUERY *view = NULL;
1182 MSIRECORD *row = NULL;
1183 UINT r;
1185 TRACE("%s\n", debugstr_w(file->File));
1187 r = MSI_OpenQuery(package->db, &view, query, file->File);
1188 if (r != ERROR_SUCCESS)
1189 goto done;
1191 r = MSI_ViewExecute(view, NULL);
1192 if (r != ERROR_SUCCESS)
1193 goto done;
1195 r = MSI_ViewFetch(view, &row);
1196 if (r != ERROR_SUCCESS)
1197 goto done;
1199 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1200 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1201 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1202 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1203 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1205 done:
1206 if (view) msiobj_release(&view->hdr);
1207 if (row) msiobj_release(&row->hdr);
1208 return r;
1211 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1213 MSIRECORD *row;
1214 static const WCHAR query[] = {
1215 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1216 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1217 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1219 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1220 if (!row)
1222 WARN("query failed\n");
1223 return ERROR_FUNCTION_FAILED;
1226 file->disk_id = MSI_RecordGetInteger( row, 1 );
1227 msiobj_release( &row->hdr );
1228 return ERROR_SUCCESS;
1231 static UINT load_file(MSIRECORD *row, LPVOID param)
1233 MSIPACKAGE* package = param;
1234 LPCWSTR component;
1235 MSIFILE *file;
1237 /* fill in the data */
1239 file = msi_alloc_zero( sizeof (MSIFILE) );
1240 if (!file)
1241 return ERROR_NOT_ENOUGH_MEMORY;
1243 file->File = msi_dup_record_field( row, 1 );
1245 component = MSI_RecordGetString( row, 2 );
1246 file->Component = msi_get_loaded_component( package, component );
1248 if (!file->Component)
1250 WARN("Component not found: %s\n", debugstr_w(component));
1251 msi_free(file->File);
1252 msi_free(file);
1253 return ERROR_SUCCESS;
1256 file->FileName = msi_dup_record_field( row, 3 );
1257 msi_reduce_to_long_filename( file->FileName );
1259 file->ShortName = msi_dup_record_field( row, 3 );
1260 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1262 file->FileSize = MSI_RecordGetInteger( row, 4 );
1263 file->Version = msi_dup_record_field( row, 5 );
1264 file->Language = msi_dup_record_field( row, 6 );
1265 file->Attributes = MSI_RecordGetInteger( row, 7 );
1266 file->Sequence = MSI_RecordGetInteger( row, 8 );
1268 file->state = msifs_invalid;
1270 /* if the compressed bits are not set in the file attributes,
1271 * then read the information from the package word count property
1273 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1275 file->IsCompressed = FALSE;
1277 else if (file->Attributes &
1278 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1280 file->IsCompressed = TRUE;
1282 else if (file->Attributes & msidbFileAttributesNoncompressed)
1284 file->IsCompressed = FALSE;
1286 else
1288 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1291 load_file_hash(package, file);
1292 load_file_disk_id(package, file);
1294 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1296 list_add_tail( &package->files, &file->entry );
1298 return ERROR_SUCCESS;
1301 static UINT load_all_files(MSIPACKAGE *package)
1303 static const WCHAR query[] = {
1304 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1305 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1306 '`','S','e','q','u','e','n','c','e','`', 0};
1307 MSIQUERY *view;
1308 UINT rc;
1310 if (!list_empty(&package->files))
1311 return ERROR_SUCCESS;
1313 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1314 if (rc != ERROR_SUCCESS)
1315 return ERROR_SUCCESS;
1317 rc = MSI_IterateRecords(view, NULL, load_file, package);
1318 msiobj_release(&view->hdr);
1319 return rc;
1322 static UINT load_media( MSIRECORD *row, LPVOID param )
1324 MSIPACKAGE *package = param;
1325 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1326 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1328 /* FIXME: load external cabinets and directory sources too */
1329 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1330 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1331 return ERROR_SUCCESS;
1334 static UINT load_all_media( MSIPACKAGE *package )
1336 static const WCHAR query[] = {
1337 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1338 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1339 '`','D','i','s','k','I','d','`',0};
1340 MSIQUERY *view;
1341 UINT r;
1343 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1344 if (r != ERROR_SUCCESS)
1345 return ERROR_SUCCESS;
1347 r = MSI_IterateRecords( view, NULL, load_media, package );
1348 msiobj_release( &view->hdr );
1349 return r;
1352 static UINT load_patch(MSIRECORD *row, LPVOID param)
1354 MSIPACKAGE *package = param;
1355 MSIFILEPATCH *patch;
1356 LPWSTR file_key;
1358 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1359 if (!patch)
1360 return ERROR_NOT_ENOUGH_MEMORY;
1362 file_key = msi_dup_record_field( row, 1 );
1363 patch->File = msi_get_loaded_file( package, file_key );
1364 msi_free(file_key);
1366 if( !patch->File )
1368 ERR("Failed to find target for patch in File table\n");
1369 msi_free(patch);
1370 return ERROR_FUNCTION_FAILED;
1373 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1375 /* FIXME: The database should be properly transformed */
1376 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1378 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1379 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1380 patch->IsApplied = FALSE;
1382 /* FIXME:
1383 * Header field - for patch validation.
1384 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1387 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1389 list_add_tail( &package->filepatches, &patch->entry );
1391 return ERROR_SUCCESS;
1394 static UINT load_all_patches(MSIPACKAGE *package)
1396 static const WCHAR query[] = {
1397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1398 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1399 '`','S','e','q','u','e','n','c','e','`',0};
1400 MSIQUERY *view;
1401 UINT rc;
1403 if (!list_empty(&package->filepatches))
1404 return ERROR_SUCCESS;
1406 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1407 if (rc != ERROR_SUCCESS)
1408 return ERROR_SUCCESS;
1410 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1411 msiobj_release(&view->hdr);
1412 return rc;
1415 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1417 static const WCHAR query[] = {
1418 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1419 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1420 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1421 MSIQUERY *view;
1423 folder->persistent = FALSE;
1424 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1426 if (!MSI_ViewExecute( view, NULL ))
1428 MSIRECORD *rec;
1429 if (!MSI_ViewFetch( view, &rec ))
1431 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1432 folder->persistent = TRUE;
1433 msiobj_release( &rec->hdr );
1436 msiobj_release( &view->hdr );
1438 return ERROR_SUCCESS;
1441 static UINT load_folder( MSIRECORD *row, LPVOID param )
1443 MSIPACKAGE *package = param;
1444 static WCHAR szEmpty[] = { 0 };
1445 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1446 MSIFOLDER *folder;
1448 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1449 list_init( &folder->children );
1450 folder->Directory = msi_dup_record_field( row, 1 );
1451 folder->Parent = msi_dup_record_field( row, 2 );
1452 p = msi_dup_record_field(row, 3);
1454 TRACE("%s\n", debugstr_w(folder->Directory));
1456 /* split src and target dir */
1457 tgt_short = p;
1458 src_short = folder_split_path( p, ':' );
1460 /* split the long and short paths */
1461 tgt_long = folder_split_path( tgt_short, '|' );
1462 src_long = folder_split_path( src_short, '|' );
1464 /* check for no-op dirs */
1465 if (tgt_short && !strcmpW( szDot, tgt_short ))
1466 tgt_short = szEmpty;
1467 if (src_short && !strcmpW( szDot, src_short ))
1468 src_short = szEmpty;
1470 if (!tgt_long)
1471 tgt_long = tgt_short;
1473 if (!src_short) {
1474 src_short = tgt_short;
1475 src_long = tgt_long;
1478 if (!src_long)
1479 src_long = src_short;
1481 /* FIXME: use the target short path too */
1482 folder->TargetDefault = strdupW(tgt_long);
1483 folder->SourceShortPath = strdupW(src_short);
1484 folder->SourceLongPath = strdupW(src_long);
1485 msi_free(p);
1487 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1488 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1489 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1491 load_folder_persistence( package, folder );
1493 list_add_tail( &package->folders, &folder->entry );
1494 return ERROR_SUCCESS;
1497 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1499 FolderList *fl;
1501 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1502 fl->folder = child;
1503 list_add_tail( &parent->children, &fl->entry );
1504 return ERROR_SUCCESS;
1507 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1509 MSIPACKAGE *package = param;
1510 MSIFOLDER *parent, *child;
1512 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1513 return ERROR_FUNCTION_FAILED;
1515 if (!child->Parent) return ERROR_SUCCESS;
1517 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1518 return ERROR_FUNCTION_FAILED;
1520 return add_folder_child( parent, child );
1523 static UINT load_all_folders( MSIPACKAGE *package )
1525 static const WCHAR query[] = {
1526 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1527 '`','D','i','r','e','c','t','o','r','y','`',0};
1528 MSIQUERY *view;
1529 UINT r;
1531 if (!list_empty(&package->folders))
1532 return ERROR_SUCCESS;
1534 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1535 if (r != ERROR_SUCCESS)
1536 return r;
1538 r = MSI_IterateRecords( view, NULL, load_folder, package );
1539 if (r != ERROR_SUCCESS)
1541 msiobj_release( &view->hdr );
1542 return r;
1544 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1545 msiobj_release( &view->hdr );
1546 return r;
1549 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1551 msi_set_property( package->db, szCostingComplete, szZero );
1552 msi_set_property( package->db, szRootDrive, szCRoot );
1554 load_all_folders( package );
1555 msi_load_all_components( package );
1556 msi_load_all_features( package );
1557 load_all_files( package );
1558 load_all_patches( package );
1559 load_all_media( package );
1561 return ERROR_SUCCESS;
1564 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1566 const WCHAR *action = package->script->Actions[script][index];
1567 ui_actionstart( package, action );
1568 TRACE("executing %s\n", debugstr_w(action));
1569 return ACTION_PerformAction( package, action, script );
1572 static UINT execute_script( MSIPACKAGE *package, UINT script )
1574 UINT i, rc = ERROR_SUCCESS;
1576 TRACE("executing script %u\n", script);
1578 if (!package->script)
1580 ERR("no script!\n");
1581 return ERROR_FUNCTION_FAILED;
1583 if (script == SCRIPT_ROLLBACK)
1585 for (i = package->script->ActionCount[script]; i > 0; i--)
1587 rc = execute_script_action( package, script, i - 1 );
1588 if (rc != ERROR_SUCCESS) break;
1591 else
1593 for (i = 0; i < package->script->ActionCount[script]; i++)
1595 rc = execute_script_action( package, script, i );
1596 if (rc != ERROR_SUCCESS) break;
1599 msi_free_action_script(package, script);
1600 return rc;
1603 static UINT ACTION_FileCost(MSIPACKAGE *package)
1605 return ERROR_SUCCESS;
1608 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1610 MSICOMPONENT *comp;
1611 UINT r;
1613 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1615 if (!comp->ComponentId) continue;
1617 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1618 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1619 &comp->Installed );
1620 if (r != ERROR_SUCCESS)
1621 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1622 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1623 &comp->Installed );
1624 if (r != ERROR_SUCCESS)
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1627 &comp->Installed );
1628 if (r != ERROR_SUCCESS)
1629 comp->Installed = INSTALLSTATE_ABSENT;
1633 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1635 MSIFEATURE *feature;
1637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1639 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1641 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1642 feature->Installed = INSTALLSTATE_ABSENT;
1643 else
1644 feature->Installed = state;
1648 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1650 return (feature->Level > 0 && feature->Level <= level);
1653 static BOOL process_state_property(MSIPACKAGE* package, int level,
1654 LPCWSTR property, INSTALLSTATE state)
1656 LPWSTR override;
1657 MSIFEATURE *feature;
1659 override = msi_dup_property( package->db, property );
1660 if (!override)
1661 return FALSE;
1663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1665 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1666 continue;
1668 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1670 if (!strcmpiW( override, szAll ))
1672 if (feature->Installed != state)
1674 feature->Action = state;
1675 feature->ActionRequest = state;
1678 else
1680 LPWSTR ptr = override;
1681 LPWSTR ptr2 = strchrW(override,',');
1683 while (ptr)
1685 int len = ptr2 - ptr;
1687 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1688 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1690 if (feature->Installed != state)
1692 feature->Action = state;
1693 feature->ActionRequest = state;
1695 break;
1697 if (ptr2)
1699 ptr=ptr2+1;
1700 ptr2 = strchrW(ptr,',');
1702 else
1703 break;
1707 msi_free(override);
1708 return TRUE;
1711 static BOOL process_overrides( MSIPACKAGE *package, int level )
1713 static const WCHAR szAddLocal[] =
1714 {'A','D','D','L','O','C','A','L',0};
1715 static const WCHAR szAddSource[] =
1716 {'A','D','D','S','O','U','R','C','E',0};
1717 static const WCHAR szAdvertise[] =
1718 {'A','D','V','E','R','T','I','S','E',0};
1719 BOOL ret = FALSE;
1721 /* all these activation/deactivation things happen in order and things
1722 * later on the list override things earlier on the list.
1724 * 0 INSTALLLEVEL processing
1725 * 1 ADDLOCAL
1726 * 2 REMOVE
1727 * 3 ADDSOURCE
1728 * 4 ADDDEFAULT
1729 * 5 REINSTALL
1730 * 6 ADVERTISE
1731 * 7 COMPADDLOCAL
1732 * 8 COMPADDSOURCE
1733 * 9 FILEADDLOCAL
1734 * 10 FILEADDSOURCE
1735 * 11 FILEADDDEFAULT
1737 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1738 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1739 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1740 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1741 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1743 if (ret)
1744 msi_set_property( package->db, szPreselected, szOne );
1746 return ret;
1749 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1751 int level;
1752 MSICOMPONENT* component;
1753 MSIFEATURE *feature;
1755 TRACE("Checking Install Level\n");
1757 level = msi_get_property_int(package->db, szInstallLevel, 1);
1759 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1761 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1763 if (!is_feature_selected( feature, level )) continue;
1765 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1767 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1769 feature->Action = INSTALLSTATE_SOURCE;
1770 feature->ActionRequest = INSTALLSTATE_SOURCE;
1772 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1774 feature->Action = INSTALLSTATE_ADVERTISED;
1775 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1777 else
1779 feature->Action = INSTALLSTATE_LOCAL;
1780 feature->ActionRequest = INSTALLSTATE_LOCAL;
1784 /* disable child features of unselected parent or follow parent */
1785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1787 FeatureList *fl;
1789 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1791 if (!is_feature_selected( feature, level ))
1793 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1794 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1796 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1798 fl->feature->Action = feature->Action;
1799 fl->feature->ActionRequest = feature->ActionRequest;
1804 else /* preselected */
1806 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1808 if (!is_feature_selected( feature, level )) continue;
1810 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1812 if (feature->Installed == INSTALLSTATE_ABSENT)
1814 feature->Action = INSTALLSTATE_UNKNOWN;
1815 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1817 else
1819 feature->Action = feature->Installed;
1820 feature->ActionRequest = feature->Installed;
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 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2290 parent = msi_get_loaded_folder( package, folder->Parent );
2291 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2293 else
2294 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2296 msi_clean_path( path );
2297 if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2299 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2300 msi_free( path );
2301 return;
2303 msi_set_property( package->db, folder->Directory, path );
2304 msi_free( folder->ResolvedTarget );
2305 folder->ResolvedTarget = path;
2307 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2309 child = fl->folder;
2310 msi_resolve_target_folder( package, child->Directory, load_prop );
2312 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2315 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2317 static const WCHAR query[] = {
2318 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2319 '`','C','o','n','d','i','t','i','o','n','`',0};
2320 static const WCHAR szOutOfDiskSpace[] = {
2321 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2322 MSICOMPONENT *comp;
2323 MSIQUERY *view;
2324 LPWSTR level;
2325 UINT rc;
2327 TRACE("Building directory properties\n");
2328 msi_resolve_target_folder( package, szTargetDir, TRUE );
2330 TRACE("Evaluating component conditions\n");
2331 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2333 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2335 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2336 comp->Enabled = FALSE;
2338 else
2339 comp->Enabled = TRUE;
2342 /* read components states from the registry */
2343 ACTION_GetComponentInstallStates(package);
2344 ACTION_GetFeatureInstallStates(package);
2346 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2348 TRACE("Evaluating feature conditions\n");
2350 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2351 if (rc == ERROR_SUCCESS)
2353 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2354 msiobj_release( &view->hdr );
2355 if (rc != ERROR_SUCCESS)
2356 return rc;
2360 TRACE("Calculating file cost\n");
2361 calculate_file_cost( package );
2363 msi_set_property( package->db, szCostingComplete, szOne );
2364 /* set default run level if not set */
2365 level = msi_dup_property( package->db, szInstallLevel );
2366 if (!level)
2367 msi_set_property( package->db, szInstallLevel, szOne );
2368 msi_free(level);
2370 /* FIXME: check volume disk space */
2371 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2373 return MSI_SetFeatureStates(package);
2376 /* OK this value is "interpreted" and then formatted based on the
2377 first few characters */
2378 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2379 DWORD *size)
2381 LPSTR data = NULL;
2383 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2385 if (value[1]=='x')
2387 LPWSTR ptr;
2388 CHAR byte[5];
2389 LPWSTR deformated = NULL;
2390 int count;
2392 deformat_string(package, &value[2], &deformated);
2394 /* binary value type */
2395 ptr = deformated;
2396 *type = REG_BINARY;
2397 if (strlenW(ptr)%2)
2398 *size = (strlenW(ptr)/2)+1;
2399 else
2400 *size = strlenW(ptr)/2;
2402 data = msi_alloc(*size);
2404 byte[0] = '0';
2405 byte[1] = 'x';
2406 byte[4] = 0;
2407 count = 0;
2408 /* if uneven pad with a zero in front */
2409 if (strlenW(ptr)%2)
2411 byte[2]= '0';
2412 byte[3]= *ptr;
2413 ptr++;
2414 data[count] = (BYTE)strtol(byte,NULL,0);
2415 count ++;
2416 TRACE("Uneven byte count\n");
2418 while (*ptr)
2420 byte[2]= *ptr;
2421 ptr++;
2422 byte[3]= *ptr;
2423 ptr++;
2424 data[count] = (BYTE)strtol(byte,NULL,0);
2425 count ++;
2427 msi_free(deformated);
2429 TRACE("Data %i bytes(%i)\n",*size,count);
2431 else
2433 LPWSTR deformated;
2434 LPWSTR p;
2435 DWORD d = 0;
2436 deformat_string(package, &value[1], &deformated);
2438 *type=REG_DWORD;
2439 *size = sizeof(DWORD);
2440 data = msi_alloc(*size);
2441 p = deformated;
2442 if (*p == '-')
2443 p++;
2444 while (*p)
2446 if ( (*p < '0') || (*p > '9') )
2447 break;
2448 d *= 10;
2449 d += (*p - '0');
2450 p++;
2452 if (deformated[0] == '-')
2453 d = -d;
2454 *(LPDWORD)data = d;
2455 TRACE("DWORD %i\n",*(LPDWORD)data);
2457 msi_free(deformated);
2460 else
2462 static const WCHAR szMulti[] = {'[','~',']',0};
2463 LPCWSTR ptr;
2464 *type=REG_SZ;
2466 if (value[0]=='#')
2468 if (value[1]=='%')
2470 ptr = &value[2];
2471 *type=REG_EXPAND_SZ;
2473 else
2474 ptr = &value[1];
2476 else
2477 ptr=value;
2479 if (strstrW(value, szMulti))
2480 *type = REG_MULTI_SZ;
2482 /* remove initial delimiter */
2483 if (!strncmpW(value, szMulti, 3))
2484 ptr = value + 3;
2486 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2488 /* add double NULL terminator */
2489 if (*type == REG_MULTI_SZ)
2491 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2492 data = msi_realloc_zero(data, *size);
2495 return data;
2498 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2500 const WCHAR *ret;
2502 switch (root)
2504 case -1:
2505 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2507 *root_key = HKEY_LOCAL_MACHINE;
2508 ret = szHLM;
2510 else
2512 *root_key = HKEY_CURRENT_USER;
2513 ret = szHCU;
2515 break;
2516 case 0:
2517 *root_key = HKEY_CLASSES_ROOT;
2518 ret = szHCR;
2519 break;
2520 case 1:
2521 *root_key = HKEY_CURRENT_USER;
2522 ret = szHCU;
2523 break;
2524 case 2:
2525 *root_key = HKEY_LOCAL_MACHINE;
2526 ret = szHLM;
2527 break;
2528 case 3:
2529 *root_key = HKEY_USERS;
2530 ret = szHU;
2531 break;
2532 default:
2533 ERR("Unknown root %i\n", root);
2534 return NULL;
2537 return ret;
2540 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2542 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2543 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2545 if (is_64bit && package->platform == PLATFORM_INTEL &&
2546 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2548 UINT size;
2549 WCHAR *path_32node;
2551 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2552 if (!(path_32node = msi_alloc( size ))) return NULL;
2554 memcpy( path_32node, path, len * sizeof(WCHAR) );
2555 strcpyW( path_32node + len, szWow6432Node );
2556 strcatW( path_32node, szBackSlash );
2557 strcatW( path_32node, path + len );
2558 return path_32node;
2561 return strdupW( path );
2564 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2566 MSIPACKAGE *package = param;
2567 LPSTR value_data = NULL;
2568 HKEY root_key, hkey;
2569 DWORD type,size;
2570 LPWSTR deformated, uikey, keypath;
2571 LPCWSTR szRoot, component, name, key, value;
2572 MSICOMPONENT *comp;
2573 MSIRECORD * uirow;
2574 INT root;
2575 BOOL check_first = FALSE;
2576 UINT rc;
2578 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2580 component = MSI_RecordGetString(row, 6);
2581 comp = msi_get_loaded_component(package,component);
2582 if (!comp)
2583 return ERROR_SUCCESS;
2585 comp->Action = msi_get_component_action( package, comp );
2586 if (comp->Action != INSTALLSTATE_LOCAL)
2588 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2589 return ERROR_SUCCESS;
2592 name = MSI_RecordGetString(row, 4);
2593 if( MSI_RecordIsNull(row,5) && name )
2595 /* null values can have special meanings */
2596 if (name[0]=='-' && name[1] == 0)
2597 return ERROR_SUCCESS;
2598 else if ((name[0]=='+' && name[1] == 0) ||
2599 (name[0] == '*' && name[1] == 0))
2600 name = NULL;
2601 check_first = TRUE;
2604 root = MSI_RecordGetInteger(row,2);
2605 key = MSI_RecordGetString(row, 3);
2607 szRoot = get_root_key( package, root, &root_key );
2608 if (!szRoot)
2609 return ERROR_SUCCESS;
2611 deformat_string(package, key , &deformated);
2612 size = strlenW(deformated) + strlenW(szRoot) + 1;
2613 uikey = msi_alloc(size*sizeof(WCHAR));
2614 strcpyW(uikey,szRoot);
2615 strcatW(uikey,deformated);
2617 keypath = get_keypath( package, root_key, deformated );
2618 msi_free( deformated );
2619 if (RegCreateKeyW( root_key, keypath, &hkey ))
2621 ERR("Could not create key %s\n", debugstr_w(keypath));
2622 msi_free(uikey);
2623 msi_free(keypath);
2624 return ERROR_SUCCESS;
2627 value = MSI_RecordGetString(row,5);
2628 if (value)
2629 value_data = parse_value(package, value, &type, &size);
2630 else
2632 value_data = (LPSTR)strdupW(szEmpty);
2633 size = sizeof(szEmpty);
2634 type = REG_SZ;
2637 deformat_string(package, name, &deformated);
2639 if (!check_first)
2641 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2642 debugstr_w(uikey));
2643 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2645 else
2647 DWORD sz = 0;
2648 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2649 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2651 TRACE("value %s of %s checked already exists\n",
2652 debugstr_w(deformated), debugstr_w(uikey));
2654 else
2656 TRACE("Checked and setting value %s of %s\n",
2657 debugstr_w(deformated), debugstr_w(uikey));
2658 if (deformated || size)
2659 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2662 RegCloseKey(hkey);
2664 uirow = MSI_CreateRecord(3);
2665 MSI_RecordSetStringW(uirow,2,deformated);
2666 MSI_RecordSetStringW(uirow,1,uikey);
2667 if (type == REG_SZ || type == REG_EXPAND_SZ)
2668 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2669 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2670 msiobj_release( &uirow->hdr );
2672 msi_free(value_data);
2673 msi_free(deformated);
2674 msi_free(uikey);
2675 msi_free(keypath);
2677 return ERROR_SUCCESS;
2680 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2682 static const WCHAR query[] = {
2683 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2684 '`','R','e','g','i','s','t','r','y','`',0};
2685 MSIQUERY *view;
2686 UINT rc;
2688 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2689 if (rc != ERROR_SUCCESS)
2690 return ERROR_SUCCESS;
2692 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2693 msiobj_release(&view->hdr);
2694 return rc;
2697 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2699 LONG res;
2700 HKEY hkey;
2701 DWORD num_subkeys, num_values;
2703 if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2705 if ((res = RegDeleteValueW( hkey, value )))
2707 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2709 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2710 NULL, NULL, NULL, NULL );
2711 RegCloseKey( hkey );
2712 if (!res && !num_subkeys && !num_values)
2714 TRACE("removing empty key %s\n", debugstr_w(keypath));
2715 RegDeleteKeyW( root, keypath );
2717 return;
2719 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2722 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2724 LONG res = RegDeleteTreeW( root, keypath );
2725 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2728 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2730 MSIPACKAGE *package = param;
2731 LPCWSTR component, name, key_str, root_key_str;
2732 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2733 MSICOMPONENT *comp;
2734 MSIRECORD *uirow;
2735 BOOL delete_key = FALSE;
2736 HKEY hkey_root;
2737 UINT size;
2738 INT root;
2740 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2742 component = MSI_RecordGetString( row, 6 );
2743 comp = msi_get_loaded_component( package, component );
2744 if (!comp)
2745 return ERROR_SUCCESS;
2747 comp->Action = msi_get_component_action( package, comp );
2748 if (comp->Action != INSTALLSTATE_ABSENT)
2750 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2751 return ERROR_SUCCESS;
2754 name = MSI_RecordGetString( row, 4 );
2755 if (MSI_RecordIsNull( row, 5 ) && name )
2757 if (name[0] == '+' && !name[1])
2758 return ERROR_SUCCESS;
2759 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2761 delete_key = TRUE;
2762 name = NULL;
2766 root = MSI_RecordGetInteger( row, 2 );
2767 key_str = MSI_RecordGetString( row, 3 );
2769 root_key_str = get_root_key( package, root, &hkey_root );
2770 if (!root_key_str)
2771 return ERROR_SUCCESS;
2773 deformat_string( package, key_str, &deformated_key );
2774 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2775 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2776 strcpyW( ui_key_str, root_key_str );
2777 strcatW( ui_key_str, deformated_key );
2779 deformat_string( package, name, &deformated_name );
2781 keypath = get_keypath( package, hkey_root, deformated_key );
2782 msi_free( deformated_key );
2783 if (delete_key) delete_reg_key( hkey_root, keypath );
2784 else delete_reg_value( hkey_root, keypath, deformated_name );
2785 msi_free( keypath );
2787 uirow = MSI_CreateRecord( 2 );
2788 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2789 MSI_RecordSetStringW( uirow, 2, deformated_name );
2790 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2791 msiobj_release( &uirow->hdr );
2793 msi_free( ui_key_str );
2794 msi_free( deformated_name );
2795 return ERROR_SUCCESS;
2798 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2800 MSIPACKAGE *package = param;
2801 LPCWSTR component, name, key_str, root_key_str;
2802 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2803 MSICOMPONENT *comp;
2804 MSIRECORD *uirow;
2805 BOOL delete_key = FALSE;
2806 HKEY hkey_root;
2807 UINT size;
2808 INT root;
2810 component = MSI_RecordGetString( row, 5 );
2811 comp = msi_get_loaded_component( package, component );
2812 if (!comp)
2813 return ERROR_SUCCESS;
2815 comp->Action = msi_get_component_action( package, comp );
2816 if (comp->Action != INSTALLSTATE_LOCAL)
2818 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2819 return ERROR_SUCCESS;
2822 if ((name = MSI_RecordGetString( row, 4 )))
2824 if (name[0] == '-' && !name[1])
2826 delete_key = TRUE;
2827 name = NULL;
2831 root = MSI_RecordGetInteger( row, 2 );
2832 key_str = MSI_RecordGetString( row, 3 );
2834 root_key_str = get_root_key( package, root, &hkey_root );
2835 if (!root_key_str)
2836 return ERROR_SUCCESS;
2838 deformat_string( package, key_str, &deformated_key );
2839 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2840 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2841 strcpyW( ui_key_str, root_key_str );
2842 strcatW( ui_key_str, deformated_key );
2844 deformat_string( package, name, &deformated_name );
2846 keypath = get_keypath( package, hkey_root, deformated_key );
2847 msi_free( deformated_key );
2848 if (delete_key) delete_reg_key( hkey_root, keypath );
2849 else delete_reg_value( hkey_root, keypath, deformated_name );
2850 msi_free( keypath );
2852 uirow = MSI_CreateRecord( 2 );
2853 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2854 MSI_RecordSetStringW( uirow, 2, deformated_name );
2855 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2856 msiobj_release( &uirow->hdr );
2858 msi_free( ui_key_str );
2859 msi_free( deformated_name );
2860 return ERROR_SUCCESS;
2863 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2865 static const WCHAR registry_query[] = {
2866 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2867 '`','R','e','g','i','s','t','r','y','`',0};
2868 static const WCHAR remove_registry_query[] = {
2869 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2870 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2871 MSIQUERY *view;
2872 UINT rc;
2874 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2875 if (rc == ERROR_SUCCESS)
2877 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2878 msiobj_release( &view->hdr );
2879 if (rc != ERROR_SUCCESS)
2880 return rc;
2882 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2883 if (rc == ERROR_SUCCESS)
2885 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2886 msiobj_release( &view->hdr );
2887 if (rc != ERROR_SUCCESS)
2888 return rc;
2890 return ERROR_SUCCESS;
2893 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2895 package->script->CurrentlyScripting = TRUE;
2897 return ERROR_SUCCESS;
2901 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2903 static const WCHAR query[]= {
2904 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2905 '`','R','e','g','i','s','t','r','y','`',0};
2906 MSICOMPONENT *comp;
2907 DWORD total = 0, count = 0;
2908 MSIQUERY *view;
2909 MSIFEATURE *feature;
2910 MSIFILE *file;
2911 UINT rc;
2913 TRACE("InstallValidate\n");
2915 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2916 if (rc == ERROR_SUCCESS)
2918 rc = MSI_IterateRecords( view, &count, NULL, package );
2919 msiobj_release( &view->hdr );
2920 if (rc != ERROR_SUCCESS)
2921 return rc;
2922 total += count * REG_PROGRESS_VALUE;
2924 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2925 total += COMPONENT_PROGRESS_VALUE;
2927 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2928 total += file->FileSize;
2930 msi_ui_progress( package, 0, total, 0, 0 );
2932 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2934 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2935 debugstr_w(feature->Feature), feature->Installed,
2936 feature->ActionRequest, feature->Action);
2938 return ERROR_SUCCESS;
2941 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2943 MSIPACKAGE* package = param;
2944 LPCWSTR cond = NULL;
2945 LPCWSTR message = NULL;
2946 UINT r;
2948 static const WCHAR title[]=
2949 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2951 cond = MSI_RecordGetString(row,1);
2953 r = MSI_EvaluateConditionW(package,cond);
2954 if (r == MSICONDITION_FALSE)
2956 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2958 LPWSTR deformated;
2959 message = MSI_RecordGetString(row,2);
2960 deformat_string(package,message,&deformated);
2961 MessageBoxW(NULL,deformated,title,MB_OK);
2962 msi_free(deformated);
2965 return ERROR_INSTALL_FAILURE;
2968 return ERROR_SUCCESS;
2971 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2973 static const WCHAR query[] = {
2974 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2975 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2976 MSIQUERY *view;
2977 UINT rc;
2979 TRACE("Checking launch conditions\n");
2981 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2982 if (rc != ERROR_SUCCESS)
2983 return ERROR_SUCCESS;
2985 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2986 msiobj_release(&view->hdr);
2987 return rc;
2990 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2993 if (!cmp->KeyPath)
2994 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2996 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2998 static const WCHAR query[] = {
2999 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3000 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3001 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3002 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3003 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3004 MSIRECORD *row;
3005 UINT root, len;
3006 LPWSTR deformated, buffer, deformated_name;
3007 LPCWSTR key, name;
3009 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3010 if (!row)
3011 return NULL;
3013 root = MSI_RecordGetInteger(row,2);
3014 key = MSI_RecordGetString(row, 3);
3015 name = MSI_RecordGetString(row, 4);
3016 deformat_string(package, key , &deformated);
3017 deformat_string(package, name, &deformated_name);
3019 len = strlenW(deformated) + 6;
3020 if (deformated_name)
3021 len+=strlenW(deformated_name);
3023 buffer = msi_alloc( len *sizeof(WCHAR));
3025 if (deformated_name)
3026 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3027 else
3028 sprintfW(buffer,fmt,root,deformated);
3030 msi_free(deformated);
3031 msi_free(deformated_name);
3032 msiobj_release(&row->hdr);
3034 return buffer;
3036 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3038 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3039 return NULL;
3041 else
3043 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3045 if (file)
3046 return strdupW( file->TargetPath );
3048 return NULL;
3051 static HKEY openSharedDLLsKey(void)
3053 HKEY hkey=0;
3054 static const WCHAR path[] =
3055 {'S','o','f','t','w','a','r','e','\\',
3056 'M','i','c','r','o','s','o','f','t','\\',
3057 'W','i','n','d','o','w','s','\\',
3058 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3059 'S','h','a','r','e','d','D','L','L','s',0};
3061 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3062 return hkey;
3065 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3067 HKEY hkey;
3068 DWORD count=0;
3069 DWORD type;
3070 DWORD sz = sizeof(count);
3071 DWORD rc;
3073 hkey = openSharedDLLsKey();
3074 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3075 if (rc != ERROR_SUCCESS)
3076 count = 0;
3077 RegCloseKey(hkey);
3078 return count;
3081 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3083 HKEY hkey;
3085 hkey = openSharedDLLsKey();
3086 if (count > 0)
3087 msi_reg_set_val_dword( hkey, path, count );
3088 else
3089 RegDeleteValueW(hkey,path);
3090 RegCloseKey(hkey);
3091 return count;
3094 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3096 MSIFEATURE *feature;
3097 INT count = 0;
3098 BOOL write = FALSE;
3100 /* only refcount DLLs */
3101 if (comp->KeyPath == NULL ||
3102 comp->assembly ||
3103 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3104 comp->Attributes & msidbComponentAttributesODBCDataSource)
3105 write = FALSE;
3106 else
3108 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3109 write = (count > 0);
3111 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3112 write = TRUE;
3115 /* increment counts */
3116 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3118 ComponentList *cl;
3120 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3121 continue;
3123 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3125 if ( cl->component == comp )
3126 count++;
3130 /* decrement counts */
3131 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3133 ComponentList *cl;
3135 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3136 continue;
3138 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3140 if ( cl->component == comp )
3141 count--;
3145 /* ref count all the files in the component */
3146 if (write)
3148 MSIFILE *file;
3150 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3152 if (file->Component == comp)
3153 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3157 /* add a count for permanent */
3158 if (comp->Attributes & msidbComponentAttributesPermanent)
3159 count ++;
3161 comp->RefCount = count;
3163 if (write)
3164 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3167 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3169 if (comp->assembly)
3171 const WCHAR prefixW[] = {'<','\\',0};
3172 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3173 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3175 if (keypath)
3177 strcpyW( keypath, prefixW );
3178 strcatW( keypath, comp->assembly->display_name );
3180 return keypath;
3182 return resolve_keypath( package, comp );
3185 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3187 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3188 UINT rc;
3189 MSICOMPONENT *comp;
3190 HKEY hkey;
3192 TRACE("\n");
3194 squash_guid(package->ProductCode,squished_pc);
3195 msi_set_sourcedir_props(package, FALSE);
3197 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3199 MSIRECORD *uirow;
3200 INSTALLSTATE action;
3202 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3203 if (!comp->ComponentId)
3204 continue;
3206 squash_guid( comp->ComponentId, squished_cc );
3207 msi_free( comp->FullKeypath );
3208 comp->FullKeypath = build_full_keypath( package, comp );
3210 ACTION_RefCountComponent( package, comp );
3212 if (package->need_rollback) action = comp->Installed;
3213 else action = comp->ActionRequest;
3215 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3216 debugstr_w(comp->Component), debugstr_w(squished_cc),
3217 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3219 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3221 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3222 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3223 else
3224 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3226 if (rc != ERROR_SUCCESS)
3227 continue;
3229 if (comp->Attributes & msidbComponentAttributesPermanent)
3231 static const WCHAR szPermKey[] =
3232 { '0','0','0','0','0','0','0','0','0','0','0','0',
3233 '0','0','0','0','0','0','0','0','0','0','0','0',
3234 '0','0','0','0','0','0','0','0',0 };
3236 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3238 if (action == INSTALLSTATE_LOCAL)
3239 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3240 else
3242 MSIFILE *file;
3243 MSIRECORD *row;
3244 LPWSTR ptr, ptr2;
3245 WCHAR source[MAX_PATH];
3246 WCHAR base[MAX_PATH];
3247 LPWSTR sourcepath;
3249 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3250 static const WCHAR query[] = {
3251 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3252 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3253 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3254 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3255 '`','D','i','s','k','I','d','`',0};
3257 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3258 continue;
3260 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3261 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3262 ptr2 = strrchrW(source, '\\') + 1;
3263 msiobj_release(&row->hdr);
3265 lstrcpyW(base, package->PackagePath);
3266 ptr = strrchrW(base, '\\');
3267 *(ptr + 1) = '\0';
3269 sourcepath = msi_resolve_file_source(package, file);
3270 ptr = sourcepath + lstrlenW(base);
3271 lstrcpyW(ptr2, ptr);
3272 msi_free(sourcepath);
3274 msi_reg_set_val_str(hkey, squished_pc, source);
3276 RegCloseKey(hkey);
3278 else if (action == INSTALLSTATE_ABSENT)
3280 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3281 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3282 else
3283 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3286 /* UI stuff */
3287 uirow = MSI_CreateRecord(3);
3288 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3289 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3290 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3291 msi_ui_actiondata( package, szProcessComponents, uirow );
3292 msiobj_release( &uirow->hdr );
3294 return ERROR_SUCCESS;
3297 typedef struct {
3298 CLSID clsid;
3299 LPWSTR source;
3301 LPWSTR path;
3302 ITypeLib *ptLib;
3303 } typelib_struct;
3305 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3306 LPWSTR lpszName, LONG_PTR lParam)
3308 TLIBATTR *attr;
3309 typelib_struct *tl_struct = (typelib_struct*) lParam;
3310 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3311 int sz;
3312 HRESULT res;
3314 if (!IS_INTRESOURCE(lpszName))
3316 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3317 return TRUE;
3320 sz = strlenW(tl_struct->source)+4;
3321 sz *= sizeof(WCHAR);
3323 if ((INT_PTR)lpszName == 1)
3324 tl_struct->path = strdupW(tl_struct->source);
3325 else
3327 tl_struct->path = msi_alloc(sz);
3328 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3331 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3332 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3333 if (FAILED(res))
3335 msi_free(tl_struct->path);
3336 tl_struct->path = NULL;
3338 return TRUE;
3341 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3342 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3344 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3345 return FALSE;
3348 msi_free(tl_struct->path);
3349 tl_struct->path = NULL;
3351 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3352 ITypeLib_Release(tl_struct->ptLib);
3354 return TRUE;
3357 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3359 MSIPACKAGE* package = param;
3360 LPCWSTR component;
3361 MSICOMPONENT *comp;
3362 MSIFILE *file;
3363 typelib_struct tl_struct;
3364 ITypeLib *tlib;
3365 HMODULE module;
3366 HRESULT hr;
3368 component = MSI_RecordGetString(row,3);
3369 comp = msi_get_loaded_component(package,component);
3370 if (!comp)
3371 return ERROR_SUCCESS;
3373 comp->Action = msi_get_component_action( package, comp );
3374 if (comp->Action != INSTALLSTATE_LOCAL)
3376 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3377 return ERROR_SUCCESS;
3380 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3382 TRACE("component has no key path\n");
3383 return ERROR_SUCCESS;
3385 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3387 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3388 if (module)
3390 LPCWSTR guid;
3391 guid = MSI_RecordGetString(row,1);
3392 CLSIDFromString( guid, &tl_struct.clsid);
3393 tl_struct.source = strdupW( file->TargetPath );
3394 tl_struct.path = NULL;
3396 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3397 (LONG_PTR)&tl_struct);
3399 if (tl_struct.path)
3401 LPCWSTR helpid, help_path = NULL;
3402 HRESULT res;
3404 helpid = MSI_RecordGetString(row,6);
3406 if (helpid) help_path = msi_get_target_folder( package, helpid );
3407 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3409 if (FAILED(res))
3410 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3411 else
3412 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3414 ITypeLib_Release(tl_struct.ptLib);
3415 msi_free(tl_struct.path);
3417 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3419 FreeLibrary(module);
3420 msi_free(tl_struct.source);
3422 else
3424 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3425 if (FAILED(hr))
3427 ERR("Failed to load type library: %08x\n", hr);
3428 return ERROR_INSTALL_FAILURE;
3431 ITypeLib_Release(tlib);
3434 return ERROR_SUCCESS;
3437 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3439 static const WCHAR query[] = {
3440 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3441 '`','T','y','p','e','L','i','b','`',0};
3442 MSIQUERY *view;
3443 UINT rc;
3445 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3446 if (rc != ERROR_SUCCESS)
3447 return ERROR_SUCCESS;
3449 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3450 msiobj_release(&view->hdr);
3451 return rc;
3454 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3456 MSIPACKAGE *package = param;
3457 LPCWSTR component, guid;
3458 MSICOMPONENT *comp;
3459 GUID libid;
3460 UINT version;
3461 LCID language;
3462 SYSKIND syskind;
3463 HRESULT hr;
3465 component = MSI_RecordGetString( row, 3 );
3466 comp = msi_get_loaded_component( package, component );
3467 if (!comp)
3468 return ERROR_SUCCESS;
3470 comp->Action = msi_get_component_action( package, comp );
3471 if (comp->Action != INSTALLSTATE_ABSENT)
3473 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3474 return ERROR_SUCCESS;
3476 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3478 guid = MSI_RecordGetString( row, 1 );
3479 CLSIDFromString( guid, &libid );
3480 version = MSI_RecordGetInteger( row, 4 );
3481 language = MSI_RecordGetInteger( row, 2 );
3483 #ifdef _WIN64
3484 syskind = SYS_WIN64;
3485 #else
3486 syskind = SYS_WIN32;
3487 #endif
3489 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3490 if (FAILED(hr))
3492 WARN("Failed to unregister typelib: %08x\n", hr);
3495 return ERROR_SUCCESS;
3498 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3500 static const WCHAR query[] = {
3501 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3502 '`','T','y','p','e','L','i','b','`',0};
3503 MSIQUERY *view;
3504 UINT rc;
3506 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3507 if (rc != ERROR_SUCCESS)
3508 return ERROR_SUCCESS;
3510 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3511 msiobj_release( &view->hdr );
3512 return rc;
3515 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3517 static const WCHAR szlnk[] = {'.','l','n','k',0};
3518 LPCWSTR directory, extension, link_folder;
3519 LPWSTR link_file, filename;
3521 directory = MSI_RecordGetString( row, 2 );
3522 link_folder = msi_get_target_folder( package, directory );
3523 if (!link_folder)
3525 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3526 return NULL;
3528 /* may be needed because of a bug somewhere else */
3529 msi_create_full_path( link_folder );
3531 filename = msi_dup_record_field( row, 3 );
3532 msi_reduce_to_long_filename( filename );
3534 extension = strchrW( filename, '.' );
3535 if (!extension || strcmpiW( extension, szlnk ))
3537 int len = strlenW( filename );
3538 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3539 memcpy( filename + len, szlnk, sizeof(szlnk) );
3541 link_file = msi_build_directory_name( 2, link_folder, filename );
3542 msi_free( filename );
3544 return link_file;
3547 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3549 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3550 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3551 WCHAR *folder, *dest, *path;
3553 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3554 folder = msi_dup_property( package->db, szWindowsFolder );
3555 else
3557 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3558 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3559 msi_free( appdata );
3561 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3562 msi_create_full_path( dest );
3563 path = msi_build_directory_name( 2, dest, icon_name );
3564 msi_free( folder );
3565 msi_free( dest );
3566 return path;
3569 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3571 MSIPACKAGE *package = param;
3572 LPWSTR link_file, deformated, path;
3573 LPCWSTR component, target;
3574 MSICOMPONENT *comp;
3575 IShellLinkW *sl = NULL;
3576 IPersistFile *pf = NULL;
3577 HRESULT res;
3579 component = MSI_RecordGetString(row, 4);
3580 comp = msi_get_loaded_component(package, component);
3581 if (!comp)
3582 return ERROR_SUCCESS;
3584 comp->Action = msi_get_component_action( package, comp );
3585 if (comp->Action != INSTALLSTATE_LOCAL)
3587 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3588 return ERROR_SUCCESS;
3590 msi_ui_actiondata( package, szCreateShortcuts, row );
3592 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3593 &IID_IShellLinkW, (LPVOID *) &sl );
3595 if (FAILED( res ))
3597 ERR("CLSID_ShellLink not available\n");
3598 goto err;
3601 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3602 if (FAILED( res ))
3604 ERR("QueryInterface(IID_IPersistFile) failed\n");
3605 goto err;
3608 target = MSI_RecordGetString(row, 5);
3609 if (strchrW(target, '['))
3611 int len;
3612 WCHAR *format_string, *p;
3614 if (!(p = strchrW( target, ']' ))) goto err;
3615 len = p - target + 1;
3616 format_string = msi_alloc( (len + 1) * sizeof(WCHAR) );
3617 memcpy( format_string, target, len * sizeof(WCHAR) );
3618 format_string[len] = 0;
3619 deformat_string( package, format_string, &deformated );
3620 msi_free( format_string );
3622 path = msi_alloc( (strlenW( deformated ) + strlenW( p + 1 ) + 2) * sizeof(WCHAR) );
3623 strcpyW( path, deformated );
3624 PathAddBackslashW( path );
3625 strcatW( path, p + 1 );
3626 TRACE("target path is %s\n", debugstr_w(path));
3628 IShellLinkW_SetPath( sl, path );
3629 msi_free( deformated );
3630 msi_free( path );
3632 else
3634 FIXME("poorly handled shortcut format, advertised shortcut\n");
3635 IShellLinkW_SetPath(sl,comp->FullKeypath);
3638 if (!MSI_RecordIsNull(row,6))
3640 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3641 deformat_string(package, arguments, &deformated);
3642 IShellLinkW_SetArguments(sl,deformated);
3643 msi_free(deformated);
3646 if (!MSI_RecordIsNull(row,7))
3648 LPCWSTR description = MSI_RecordGetString(row, 7);
3649 IShellLinkW_SetDescription(sl, description);
3652 if (!MSI_RecordIsNull(row,8))
3653 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3655 if (!MSI_RecordIsNull(row,9))
3657 INT index;
3658 LPCWSTR icon = MSI_RecordGetString(row, 9);
3660 path = msi_build_icon_path(package, icon);
3661 index = MSI_RecordGetInteger(row,10);
3663 /* no value means 0 */
3664 if (index == MSI_NULL_INTEGER)
3665 index = 0;
3667 IShellLinkW_SetIconLocation(sl, path, index);
3668 msi_free(path);
3671 if (!MSI_RecordIsNull(row,11))
3672 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3674 if (!MSI_RecordIsNull(row,12))
3676 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3677 full_path = msi_get_target_folder( package, wkdir );
3678 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3680 link_file = get_link_file(package, row);
3682 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3683 IPersistFile_Save(pf, link_file, FALSE);
3684 msi_free(link_file);
3686 err:
3687 if (pf)
3688 IPersistFile_Release( pf );
3689 if (sl)
3690 IShellLinkW_Release( sl );
3692 return ERROR_SUCCESS;
3695 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3697 static const WCHAR query[] = {
3698 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3699 '`','S','h','o','r','t','c','u','t','`',0};
3700 MSIQUERY *view;
3701 HRESULT res;
3702 UINT rc;
3704 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3705 if (rc != ERROR_SUCCESS)
3706 return ERROR_SUCCESS;
3708 res = CoInitialize( NULL );
3710 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3711 msiobj_release(&view->hdr);
3713 if (SUCCEEDED(res)) CoUninitialize();
3714 return rc;
3717 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3719 MSIPACKAGE *package = param;
3720 LPWSTR link_file;
3721 LPCWSTR component;
3722 MSICOMPONENT *comp;
3724 component = MSI_RecordGetString( row, 4 );
3725 comp = msi_get_loaded_component( package, component );
3726 if (!comp)
3727 return ERROR_SUCCESS;
3729 comp->Action = msi_get_component_action( package, comp );
3730 if (comp->Action != INSTALLSTATE_ABSENT)
3732 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3733 return ERROR_SUCCESS;
3735 msi_ui_actiondata( package, szRemoveShortcuts, row );
3737 link_file = get_link_file( package, row );
3739 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3740 if (!DeleteFileW( link_file ))
3742 WARN("Failed to remove shortcut file %u\n", GetLastError());
3744 msi_free( link_file );
3746 return ERROR_SUCCESS;
3749 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3751 static const WCHAR query[] = {
3752 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3753 '`','S','h','o','r','t','c','u','t','`',0};
3754 MSIQUERY *view;
3755 UINT rc;
3757 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3758 if (rc != ERROR_SUCCESS)
3759 return ERROR_SUCCESS;
3761 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3762 msiobj_release( &view->hdr );
3763 return rc;
3766 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3768 MSIPACKAGE* package = param;
3769 HANDLE the_file;
3770 LPWSTR FilePath;
3771 LPCWSTR FileName;
3772 CHAR buffer[1024];
3773 DWORD sz;
3774 UINT rc;
3776 FileName = MSI_RecordGetString(row,1);
3777 if (!FileName)
3779 ERR("Unable to get FileName\n");
3780 return ERROR_SUCCESS;
3783 FilePath = msi_build_icon_path(package, FileName);
3785 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3787 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3788 FILE_ATTRIBUTE_NORMAL, NULL);
3790 if (the_file == INVALID_HANDLE_VALUE)
3792 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3793 msi_free(FilePath);
3794 return ERROR_SUCCESS;
3799 DWORD write;
3800 sz = 1024;
3801 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3802 if (rc != ERROR_SUCCESS)
3804 ERR("Failed to get stream\n");
3805 CloseHandle(the_file);
3806 DeleteFileW(FilePath);
3807 break;
3809 WriteFile(the_file,buffer,sz,&write,NULL);
3810 } while (sz == 1024);
3812 msi_free(FilePath);
3813 CloseHandle(the_file);
3815 return ERROR_SUCCESS;
3818 static UINT msi_publish_icons(MSIPACKAGE *package)
3820 static const WCHAR query[]= {
3821 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3822 '`','I','c','o','n','`',0};
3823 MSIQUERY *view;
3824 UINT r;
3826 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3827 if (r == ERROR_SUCCESS)
3829 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3830 msiobj_release(&view->hdr);
3831 if (r != ERROR_SUCCESS)
3832 return r;
3834 return ERROR_SUCCESS;
3837 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3839 UINT r;
3840 HKEY source;
3841 LPWSTR buffer;
3842 MSIMEDIADISK *disk;
3843 MSISOURCELISTINFO *info;
3845 r = RegCreateKeyW(hkey, szSourceList, &source);
3846 if (r != ERROR_SUCCESS)
3847 return r;
3849 RegCloseKey(source);
3851 buffer = strrchrW(package->PackagePath, '\\') + 1;
3852 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3853 package->Context, MSICODE_PRODUCT,
3854 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3855 if (r != ERROR_SUCCESS)
3856 return r;
3858 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3859 package->Context, MSICODE_PRODUCT,
3860 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3861 if (r != ERROR_SUCCESS)
3862 return r;
3864 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3865 package->Context, MSICODE_PRODUCT,
3866 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3867 if (r != ERROR_SUCCESS)
3868 return r;
3870 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3872 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3873 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3874 info->options, info->value);
3875 else
3876 MsiSourceListSetInfoW(package->ProductCode, NULL,
3877 info->context, info->options,
3878 info->property, info->value);
3881 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3883 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3884 disk->context, disk->options,
3885 disk->disk_id, disk->volume_label, disk->disk_prompt);
3888 return ERROR_SUCCESS;
3891 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3893 MSIHANDLE hdb, suminfo;
3894 WCHAR guids[MAX_PATH];
3895 WCHAR packcode[SQUISH_GUID_SIZE];
3896 LPWSTR buffer;
3897 LPWSTR ptr;
3898 DWORD langid;
3899 DWORD size;
3900 UINT r;
3902 static const WCHAR szARPProductIcon[] =
3903 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3904 static const WCHAR szAssignment[] =
3905 {'A','s','s','i','g','n','m','e','n','t',0};
3906 static const WCHAR szAdvertiseFlags[] =
3907 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3908 static const WCHAR szClients[] =
3909 {'C','l','i','e','n','t','s',0};
3910 static const WCHAR szColon[] = {':',0};
3912 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3913 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3914 msi_free(buffer);
3916 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3917 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3919 /* FIXME */
3920 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3922 buffer = msi_dup_property(package->db, szARPProductIcon);
3923 if (buffer)
3925 LPWSTR path = msi_build_icon_path(package, buffer);
3926 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3927 msi_free(path);
3928 msi_free(buffer);
3931 buffer = msi_dup_property(package->db, szProductVersion);
3932 if (buffer)
3934 DWORD verdword = msi_version_str_to_dword(buffer);
3935 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3936 msi_free(buffer);
3939 msi_reg_set_val_dword(hkey, szAssignment, 0);
3940 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3941 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3942 msi_reg_set_val_str(hkey, szClients, szColon);
3944 hdb = alloc_msihandle(&package->db->hdr);
3945 if (!hdb)
3946 return ERROR_NOT_ENOUGH_MEMORY;
3948 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3949 MsiCloseHandle(hdb);
3950 if (r != ERROR_SUCCESS)
3951 goto done;
3953 size = MAX_PATH;
3954 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3955 NULL, guids, &size);
3956 if (r != ERROR_SUCCESS)
3957 goto done;
3959 ptr = strchrW(guids, ';');
3960 if (ptr) *ptr = 0;
3961 squash_guid(guids, packcode);
3962 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3964 done:
3965 MsiCloseHandle(suminfo);
3966 return ERROR_SUCCESS;
3969 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3971 UINT r;
3972 HKEY hkey;
3973 LPWSTR upgrade;
3974 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3976 upgrade = msi_dup_property(package->db, szUpgradeCode);
3977 if (!upgrade)
3978 return ERROR_SUCCESS;
3980 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3981 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3982 else
3983 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3985 if (r != ERROR_SUCCESS)
3987 WARN("failed to open upgrade code key\n");
3988 msi_free(upgrade);
3989 return ERROR_SUCCESS;
3991 squash_guid(package->ProductCode, squashed_pc);
3992 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3993 RegCloseKey(hkey);
3994 msi_free(upgrade);
3995 return ERROR_SUCCESS;
3998 static BOOL msi_check_publish(MSIPACKAGE *package)
4000 MSIFEATURE *feature;
4002 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4004 feature->Action = msi_get_feature_action( package, feature );
4005 if (feature->Action == INSTALLSTATE_LOCAL)
4006 return TRUE;
4009 return FALSE;
4012 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4014 MSIFEATURE *feature;
4016 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4018 feature->Action = msi_get_feature_action( package, feature );
4019 if (feature->Action != INSTALLSTATE_ABSENT)
4020 return FALSE;
4023 return TRUE;
4026 static UINT msi_publish_patches( MSIPACKAGE *package )
4028 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4029 WCHAR patch_squashed[GUID_SIZE];
4030 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4031 LONG res;
4032 MSIPATCHINFO *patch;
4033 UINT r;
4034 WCHAR *p, *all_patches = NULL;
4035 DWORD len = 0;
4037 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4038 if (r != ERROR_SUCCESS)
4039 return ERROR_FUNCTION_FAILED;
4041 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4042 if (res != ERROR_SUCCESS)
4044 r = ERROR_FUNCTION_FAILED;
4045 goto done;
4048 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4049 if (r != ERROR_SUCCESS)
4050 goto done;
4052 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4054 squash_guid( patch->patchcode, patch_squashed );
4055 len += strlenW( patch_squashed ) + 1;
4058 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4059 if (!all_patches)
4060 goto done;
4062 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4064 HKEY patch_key;
4066 squash_guid( patch->patchcode, p );
4067 p += strlenW( p ) + 1;
4069 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4070 (const BYTE *)patch->transforms,
4071 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4072 if (res != ERROR_SUCCESS)
4073 goto done;
4075 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4076 if (r != ERROR_SUCCESS)
4077 goto done;
4079 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4080 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4081 RegCloseKey( patch_key );
4082 if (res != ERROR_SUCCESS)
4083 goto done;
4085 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4087 res = GetLastError();
4088 ERR("Unable to copy patch package %d\n", res);
4089 goto done;
4091 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4092 if (res != ERROR_SUCCESS)
4093 goto done;
4095 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4096 RegCloseKey( patch_key );
4097 if (res != ERROR_SUCCESS)
4098 goto done;
4101 all_patches[len] = 0;
4102 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4103 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4104 if (res != ERROR_SUCCESS)
4105 goto done;
4107 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4108 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4109 if (res != ERROR_SUCCESS)
4110 r = ERROR_FUNCTION_FAILED;
4112 done:
4113 RegCloseKey( product_patches_key );
4114 RegCloseKey( patches_key );
4115 RegCloseKey( product_key );
4116 msi_free( all_patches );
4117 return r;
4120 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4122 UINT rc;
4123 HKEY hukey = NULL, hudkey = NULL;
4124 MSIRECORD *uirow;
4126 if (!list_empty(&package->patches))
4128 rc = msi_publish_patches(package);
4129 if (rc != ERROR_SUCCESS)
4130 goto end;
4133 /* FIXME: also need to publish if the product is in advertise mode */
4134 if (!msi_check_publish(package))
4135 return ERROR_SUCCESS;
4137 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4138 &hukey, TRUE);
4139 if (rc != ERROR_SUCCESS)
4140 goto end;
4142 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4143 NULL, &hudkey, TRUE);
4144 if (rc != ERROR_SUCCESS)
4145 goto end;
4147 rc = msi_publish_upgrade_code(package);
4148 if (rc != ERROR_SUCCESS)
4149 goto end;
4151 rc = msi_publish_product_properties(package, hukey);
4152 if (rc != ERROR_SUCCESS)
4153 goto end;
4155 rc = msi_publish_sourcelist(package, hukey);
4156 if (rc != ERROR_SUCCESS)
4157 goto end;
4159 rc = msi_publish_icons(package);
4161 end:
4162 uirow = MSI_CreateRecord( 1 );
4163 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4164 msi_ui_actiondata( package, szPublishProduct, uirow );
4165 msiobj_release( &uirow->hdr );
4167 RegCloseKey(hukey);
4168 RegCloseKey(hudkey);
4169 return rc;
4172 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4174 WCHAR *filename, *ptr, *folder, *ret;
4175 const WCHAR *dirprop;
4177 filename = msi_dup_record_field( row, 2 );
4178 if (filename && (ptr = strchrW( filename, '|' )))
4179 ptr++;
4180 else
4181 ptr = filename;
4183 dirprop = MSI_RecordGetString( row, 3 );
4184 if (dirprop)
4186 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4187 if (!folder) folder = msi_dup_property( package->db, dirprop );
4189 else
4190 folder = msi_dup_property( package->db, szWindowsFolder );
4192 if (!folder)
4194 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4195 msi_free( filename );
4196 return NULL;
4199 ret = msi_build_directory_name( 2, folder, ptr );
4201 msi_free( filename );
4202 msi_free( folder );
4203 return ret;
4206 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4208 MSIPACKAGE *package = param;
4209 LPCWSTR component, section, key, value, identifier;
4210 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4211 MSIRECORD * uirow;
4212 INT action;
4213 MSICOMPONENT *comp;
4215 component = MSI_RecordGetString(row, 8);
4216 comp = msi_get_loaded_component(package,component);
4217 if (!comp)
4218 return ERROR_SUCCESS;
4220 comp->Action = msi_get_component_action( package, comp );
4221 if (comp->Action != INSTALLSTATE_LOCAL)
4223 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4224 return ERROR_SUCCESS;
4227 identifier = MSI_RecordGetString(row,1);
4228 section = MSI_RecordGetString(row,4);
4229 key = MSI_RecordGetString(row,5);
4230 value = MSI_RecordGetString(row,6);
4231 action = MSI_RecordGetInteger(row,7);
4233 deformat_string(package,section,&deformated_section);
4234 deformat_string(package,key,&deformated_key);
4235 deformat_string(package,value,&deformated_value);
4237 fullname = get_ini_file_name(package, row);
4239 if (action == 0)
4241 TRACE("Adding value %s to section %s in %s\n",
4242 debugstr_w(deformated_key), debugstr_w(deformated_section),
4243 debugstr_w(fullname));
4244 WritePrivateProfileStringW(deformated_section, deformated_key,
4245 deformated_value, fullname);
4247 else if (action == 1)
4249 WCHAR returned[10];
4250 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4251 returned, 10, fullname);
4252 if (returned[0] == 0)
4254 TRACE("Adding value %s to section %s in %s\n",
4255 debugstr_w(deformated_key), debugstr_w(deformated_section),
4256 debugstr_w(fullname));
4258 WritePrivateProfileStringW(deformated_section, deformated_key,
4259 deformated_value, fullname);
4262 else if (action == 3)
4263 FIXME("Append to existing section not yet implemented\n");
4265 uirow = MSI_CreateRecord(4);
4266 MSI_RecordSetStringW(uirow,1,identifier);
4267 MSI_RecordSetStringW(uirow,2,deformated_section);
4268 MSI_RecordSetStringW(uirow,3,deformated_key);
4269 MSI_RecordSetStringW(uirow,4,deformated_value);
4270 msi_ui_actiondata( package, szWriteIniValues, uirow );
4271 msiobj_release( &uirow->hdr );
4273 msi_free(fullname);
4274 msi_free(deformated_key);
4275 msi_free(deformated_value);
4276 msi_free(deformated_section);
4277 return ERROR_SUCCESS;
4280 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4282 static const WCHAR query[] = {
4283 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4284 '`','I','n','i','F','i','l','e','`',0};
4285 MSIQUERY *view;
4286 UINT rc;
4288 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4289 if (rc != ERROR_SUCCESS)
4290 return ERROR_SUCCESS;
4292 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4293 msiobj_release(&view->hdr);
4294 return rc;
4297 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4299 MSIPACKAGE *package = param;
4300 LPCWSTR component, section, key, value, identifier;
4301 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4302 MSICOMPONENT *comp;
4303 MSIRECORD *uirow;
4304 INT action;
4306 component = MSI_RecordGetString( row, 8 );
4307 comp = msi_get_loaded_component( package, component );
4308 if (!comp)
4309 return ERROR_SUCCESS;
4311 comp->Action = msi_get_component_action( package, comp );
4312 if (comp->Action != INSTALLSTATE_ABSENT)
4314 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4315 return ERROR_SUCCESS;
4318 identifier = MSI_RecordGetString( row, 1 );
4319 section = MSI_RecordGetString( row, 4 );
4320 key = MSI_RecordGetString( row, 5 );
4321 value = MSI_RecordGetString( row, 6 );
4322 action = MSI_RecordGetInteger( row, 7 );
4324 deformat_string( package, section, &deformated_section );
4325 deformat_string( package, key, &deformated_key );
4326 deformat_string( package, value, &deformated_value );
4328 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4330 filename = get_ini_file_name( package, row );
4332 TRACE("Removing key %s from section %s in %s\n",
4333 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4335 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4337 WARN("Unable to remove key %u\n", GetLastError());
4339 msi_free( filename );
4341 else
4342 FIXME("Unsupported action %d\n", action);
4345 uirow = MSI_CreateRecord( 4 );
4346 MSI_RecordSetStringW( uirow, 1, identifier );
4347 MSI_RecordSetStringW( uirow, 2, deformated_section );
4348 MSI_RecordSetStringW( uirow, 3, deformated_key );
4349 MSI_RecordSetStringW( uirow, 4, deformated_value );
4350 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4351 msiobj_release( &uirow->hdr );
4353 msi_free( deformated_key );
4354 msi_free( deformated_value );
4355 msi_free( deformated_section );
4356 return ERROR_SUCCESS;
4359 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4361 MSIPACKAGE *package = param;
4362 LPCWSTR component, section, key, value, identifier;
4363 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4364 MSICOMPONENT *comp;
4365 MSIRECORD *uirow;
4366 INT action;
4368 component = MSI_RecordGetString( row, 8 );
4369 comp = msi_get_loaded_component( package, component );
4370 if (!comp)
4371 return ERROR_SUCCESS;
4373 comp->Action = msi_get_component_action( package, comp );
4374 if (comp->Action != INSTALLSTATE_LOCAL)
4376 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4377 return ERROR_SUCCESS;
4380 identifier = MSI_RecordGetString( row, 1 );
4381 section = MSI_RecordGetString( row, 4 );
4382 key = MSI_RecordGetString( row, 5 );
4383 value = MSI_RecordGetString( row, 6 );
4384 action = MSI_RecordGetInteger( row, 7 );
4386 deformat_string( package, section, &deformated_section );
4387 deformat_string( package, key, &deformated_key );
4388 deformat_string( package, value, &deformated_value );
4390 if (action == msidbIniFileActionRemoveLine)
4392 filename = get_ini_file_name( package, row );
4394 TRACE("Removing key %s from section %s in %s\n",
4395 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4397 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4399 WARN("Unable to remove key %u\n", GetLastError());
4401 msi_free( filename );
4403 else
4404 FIXME("Unsupported action %d\n", action);
4406 uirow = MSI_CreateRecord( 4 );
4407 MSI_RecordSetStringW( uirow, 1, identifier );
4408 MSI_RecordSetStringW( uirow, 2, deformated_section );
4409 MSI_RecordSetStringW( uirow, 3, deformated_key );
4410 MSI_RecordSetStringW( uirow, 4, deformated_value );
4411 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4412 msiobj_release( &uirow->hdr );
4414 msi_free( deformated_key );
4415 msi_free( deformated_value );
4416 msi_free( deformated_section );
4417 return ERROR_SUCCESS;
4420 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4422 static const WCHAR query[] = {
4423 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4424 '`','I','n','i','F','i','l','e','`',0};
4425 static const WCHAR remove_query[] = {
4426 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4427 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4428 MSIQUERY *view;
4429 UINT rc;
4431 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4432 if (rc == ERROR_SUCCESS)
4434 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4435 msiobj_release( &view->hdr );
4436 if (rc != ERROR_SUCCESS)
4437 return rc;
4439 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4440 if (rc == ERROR_SUCCESS)
4442 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4443 msiobj_release( &view->hdr );
4444 if (rc != ERROR_SUCCESS)
4445 return rc;
4447 return ERROR_SUCCESS;
4450 static void register_dll( const WCHAR *dll, BOOL unregister )
4452 HMODULE hmod;
4454 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4455 if (hmod)
4457 HRESULT (WINAPI *func_ptr)( void );
4458 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4460 func_ptr = (void *)GetProcAddress( hmod, func );
4461 if (func_ptr)
4463 HRESULT hr = func_ptr();
4464 if (FAILED( hr ))
4465 WARN("failed to register dll 0x%08x\n", hr);
4467 else
4468 WARN("entry point %s not found\n", func);
4469 FreeLibrary( hmod );
4470 return;
4472 WARN("failed to load library %u\n", GetLastError());
4475 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4477 MSIPACKAGE *package = param;
4478 LPCWSTR filename;
4479 MSIFILE *file;
4480 MSIRECORD *uirow;
4482 filename = MSI_RecordGetString(row,1);
4483 file = msi_get_loaded_file( package, filename );
4484 if (!file)
4486 WARN("unable to find file %s\n", debugstr_w(filename));
4487 return ERROR_SUCCESS;
4489 file->Component->Action = msi_get_component_action( package, file->Component );
4490 if (file->Component->Action != INSTALLSTATE_LOCAL)
4492 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4493 return ERROR_SUCCESS;
4496 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4497 register_dll( file->TargetPath, FALSE );
4499 uirow = MSI_CreateRecord( 2 );
4500 MSI_RecordSetStringW( uirow, 1, filename );
4501 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4502 msi_ui_actiondata( package, szSelfRegModules, uirow );
4503 msiobj_release( &uirow->hdr );
4505 return ERROR_SUCCESS;
4508 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4510 static const WCHAR query[] = {
4511 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4512 '`','S','e','l','f','R','e','g','`',0};
4513 MSIQUERY *view;
4514 UINT rc;
4516 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4517 if (rc != ERROR_SUCCESS)
4518 return ERROR_SUCCESS;
4520 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4521 msiobj_release(&view->hdr);
4522 return rc;
4525 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4527 MSIPACKAGE *package = param;
4528 LPCWSTR filename;
4529 MSIFILE *file;
4530 MSIRECORD *uirow;
4532 filename = MSI_RecordGetString( row, 1 );
4533 file = msi_get_loaded_file( package, filename );
4534 if (!file)
4536 WARN("unable to find file %s\n", debugstr_w(filename));
4537 return ERROR_SUCCESS;
4539 file->Component->Action = msi_get_component_action( package, file->Component );
4540 if (file->Component->Action != INSTALLSTATE_ABSENT)
4542 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4543 return ERROR_SUCCESS;
4546 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4547 register_dll( file->TargetPath, TRUE );
4549 uirow = MSI_CreateRecord( 2 );
4550 MSI_RecordSetStringW( uirow, 1, filename );
4551 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4552 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4553 msiobj_release( &uirow->hdr );
4555 return ERROR_SUCCESS;
4558 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4560 static const WCHAR query[] = {
4561 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4562 '`','S','e','l','f','R','e','g','`',0};
4563 MSIQUERY *view;
4564 UINT rc;
4566 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4567 if (rc != ERROR_SUCCESS)
4568 return ERROR_SUCCESS;
4570 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4571 msiobj_release( &view->hdr );
4572 return rc;
4575 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4577 MSIFEATURE *feature;
4578 UINT rc;
4579 HKEY hkey = NULL, userdata = NULL;
4581 if (!msi_check_publish(package))
4582 return ERROR_SUCCESS;
4584 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4585 &hkey, TRUE);
4586 if (rc != ERROR_SUCCESS)
4587 goto end;
4589 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4590 &userdata, TRUE);
4591 if (rc != ERROR_SUCCESS)
4592 goto end;
4594 /* here the guids are base 85 encoded */
4595 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4597 ComponentList *cl;
4598 LPWSTR data = NULL;
4599 GUID clsid;
4600 INT size;
4601 BOOL absent = FALSE;
4602 MSIRECORD *uirow;
4604 if (feature->Action != INSTALLSTATE_LOCAL &&
4605 feature->Action != INSTALLSTATE_SOURCE &&
4606 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4608 size = 1;
4609 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4611 size += 21;
4613 if (feature->Feature_Parent)
4614 size += strlenW( feature->Feature_Parent )+2;
4616 data = msi_alloc(size * sizeof(WCHAR));
4618 data[0] = 0;
4619 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4621 MSICOMPONENT* component = cl->component;
4622 WCHAR buf[21];
4624 buf[0] = 0;
4625 if (component->ComponentId)
4627 TRACE("From %s\n",debugstr_w(component->ComponentId));
4628 CLSIDFromString(component->ComponentId, &clsid);
4629 encode_base85_guid(&clsid,buf);
4630 TRACE("to %s\n",debugstr_w(buf));
4631 strcatW(data,buf);
4635 if (feature->Feature_Parent)
4637 static const WCHAR sep[] = {'\2',0};
4638 strcatW(data,sep);
4639 strcatW(data,feature->Feature_Parent);
4642 msi_reg_set_val_str( userdata, feature->Feature, data );
4643 msi_free(data);
4645 size = 0;
4646 if (feature->Feature_Parent)
4647 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4648 if (!absent)
4650 size += sizeof(WCHAR);
4651 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4652 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4654 else
4656 size += 2*sizeof(WCHAR);
4657 data = msi_alloc(size);
4658 data[0] = 0x6;
4659 data[1] = 0;
4660 if (feature->Feature_Parent)
4661 strcpyW( &data[1], feature->Feature_Parent );
4662 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4663 (LPBYTE)data,size);
4664 msi_free(data);
4667 /* the UI chunk */
4668 uirow = MSI_CreateRecord( 1 );
4669 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4670 msi_ui_actiondata( package, szPublishFeatures, uirow );
4671 msiobj_release( &uirow->hdr );
4672 /* FIXME: call msi_ui_progress? */
4675 end:
4676 RegCloseKey(hkey);
4677 RegCloseKey(userdata);
4678 return rc;
4681 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4683 UINT r;
4684 HKEY hkey;
4685 MSIRECORD *uirow;
4687 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4689 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4690 &hkey, FALSE);
4691 if (r == ERROR_SUCCESS)
4693 RegDeleteValueW(hkey, feature->Feature);
4694 RegCloseKey(hkey);
4697 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4698 &hkey, FALSE);
4699 if (r == ERROR_SUCCESS)
4701 RegDeleteValueW(hkey, feature->Feature);
4702 RegCloseKey(hkey);
4705 uirow = MSI_CreateRecord( 1 );
4706 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4707 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4708 msiobj_release( &uirow->hdr );
4710 return ERROR_SUCCESS;
4713 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4715 MSIFEATURE *feature;
4717 if (!msi_check_unpublish(package))
4718 return ERROR_SUCCESS;
4720 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4722 msi_unpublish_feature(package, feature);
4725 return ERROR_SUCCESS;
4728 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4730 SYSTEMTIME systime;
4731 DWORD size, langid;
4732 WCHAR date[9], *val, *buffer;
4733 const WCHAR *prop, *key;
4735 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4736 static const WCHAR modpath_fmt[] =
4737 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4738 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4739 static const WCHAR szModifyPath[] =
4740 {'M','o','d','i','f','y','P','a','t','h',0};
4741 static const WCHAR szUninstallString[] =
4742 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4743 static const WCHAR szEstimatedSize[] =
4744 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4745 static const WCHAR szDisplayVersion[] =
4746 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4747 static const WCHAR szInstallSource[] =
4748 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4749 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4750 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4751 static const WCHAR szAuthorizedCDFPrefix[] =
4752 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4753 static const WCHAR szARPCONTACT[] =
4754 {'A','R','P','C','O','N','T','A','C','T',0};
4755 static const WCHAR szContact[] =
4756 {'C','o','n','t','a','c','t',0};
4757 static const WCHAR szARPCOMMENTS[] =
4758 {'A','R','P','C','O','M','M','E','N','T','S',0};
4759 static const WCHAR szComments[] =
4760 {'C','o','m','m','e','n','t','s',0};
4761 static const WCHAR szProductName[] =
4762 {'P','r','o','d','u','c','t','N','a','m','e',0};
4763 static const WCHAR szDisplayName[] =
4764 {'D','i','s','p','l','a','y','N','a','m','e',0};
4765 static const WCHAR szARPHELPLINK[] =
4766 {'A','R','P','H','E','L','P','L','I','N','K',0};
4767 static const WCHAR szHelpLink[] =
4768 {'H','e','l','p','L','i','n','k',0};
4769 static const WCHAR szARPHELPTELEPHONE[] =
4770 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4771 static const WCHAR szHelpTelephone[] =
4772 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4773 static const WCHAR szARPINSTALLLOCATION[] =
4774 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4775 static const WCHAR szInstallLocation[] =
4776 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4777 static const WCHAR szManufacturer[] =
4778 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4779 static const WCHAR szPublisher[] =
4780 {'P','u','b','l','i','s','h','e','r',0};
4781 static const WCHAR szARPREADME[] =
4782 {'A','R','P','R','E','A','D','M','E',0};
4783 static const WCHAR szReadme[] =
4784 {'R','e','a','d','M','e',0};
4785 static const WCHAR szARPSIZE[] =
4786 {'A','R','P','S','I','Z','E',0};
4787 static const WCHAR szSize[] =
4788 {'S','i','z','e',0};
4789 static const WCHAR szARPURLINFOABOUT[] =
4790 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4791 static const WCHAR szURLInfoAbout[] =
4792 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4793 static const WCHAR szARPURLUPDATEINFO[] =
4794 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4795 static const WCHAR szURLUpdateInfo[] =
4796 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4797 static const WCHAR szARPSYSTEMCOMPONENT[] =
4798 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4799 static const WCHAR szSystemComponent[] =
4800 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4802 static const WCHAR *propval[] = {
4803 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4804 szARPCONTACT, szContact,
4805 szARPCOMMENTS, szComments,
4806 szProductName, szDisplayName,
4807 szARPHELPLINK, szHelpLink,
4808 szARPHELPTELEPHONE, szHelpTelephone,
4809 szARPINSTALLLOCATION, szInstallLocation,
4810 szSourceDir, szInstallSource,
4811 szManufacturer, szPublisher,
4812 szARPREADME, szReadme,
4813 szARPSIZE, szSize,
4814 szARPURLINFOABOUT, szURLInfoAbout,
4815 szARPURLUPDATEINFO, szURLUpdateInfo,
4816 NULL
4818 const WCHAR **p = propval;
4820 while (*p)
4822 prop = *p++;
4823 key = *p++;
4824 val = msi_dup_property(package->db, prop);
4825 msi_reg_set_val_str(hkey, key, val);
4826 msi_free(val);
4829 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4830 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4832 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4834 size = deformat_string(package, modpath_fmt, &buffer);
4835 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4836 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4837 msi_free(buffer);
4839 /* FIXME: Write real Estimated Size when we have it */
4840 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4842 GetLocalTime(&systime);
4843 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4844 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4846 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4847 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4849 buffer = msi_dup_property(package->db, szProductVersion);
4850 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4851 if (buffer)
4853 DWORD verdword = msi_version_str_to_dword(buffer);
4855 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4856 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4857 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4858 msi_free(buffer);
4861 return ERROR_SUCCESS;
4864 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4866 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4867 MSIRECORD *uirow;
4868 LPWSTR upgrade_code;
4869 HKEY hkey, props, upgrade_key;
4870 UINT rc;
4872 /* FIXME: also need to publish if the product is in advertise mode */
4873 if (!msi_check_publish(package))
4874 return ERROR_SUCCESS;
4876 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4877 if (rc != ERROR_SUCCESS)
4878 return rc;
4880 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4881 if (rc != ERROR_SUCCESS)
4882 goto done;
4884 rc = msi_publish_install_properties(package, hkey);
4885 if (rc != ERROR_SUCCESS)
4886 goto done;
4888 rc = msi_publish_install_properties(package, props);
4889 if (rc != ERROR_SUCCESS)
4890 goto done;
4892 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4893 if (upgrade_code)
4895 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4896 if (rc == ERROR_SUCCESS)
4898 squash_guid( package->ProductCode, squashed_pc );
4899 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4900 RegCloseKey( upgrade_key );
4902 msi_free( upgrade_code );
4904 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4905 package->delete_on_close = FALSE;
4907 done:
4908 uirow = MSI_CreateRecord( 1 );
4909 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4910 msi_ui_actiondata( package, szRegisterProduct, uirow );
4911 msiobj_release( &uirow->hdr );
4913 RegCloseKey(hkey);
4914 return ERROR_SUCCESS;
4917 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4919 return execute_script(package, SCRIPT_INSTALL);
4922 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4924 MSIPACKAGE *package = param;
4925 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4926 WCHAR *p, *icon_path;
4928 if (!icon) return ERROR_SUCCESS;
4929 if ((icon_path = msi_build_icon_path( package, icon )))
4931 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4932 DeleteFileW( icon_path );
4933 if ((p = strrchrW( icon_path, '\\' )))
4935 *p = 0;
4936 RemoveDirectoryW( icon_path );
4938 msi_free( icon_path );
4940 return ERROR_SUCCESS;
4943 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4945 static const WCHAR query[]= {
4946 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4947 MSIQUERY *view;
4948 UINT r;
4950 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4951 if (r == ERROR_SUCCESS)
4953 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4954 msiobj_release( &view->hdr );
4955 if (r != ERROR_SUCCESS)
4956 return r;
4958 return ERROR_SUCCESS;
4961 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4963 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4964 WCHAR *upgrade, **features;
4965 BOOL full_uninstall = TRUE;
4966 MSIFEATURE *feature;
4967 MSIPATCHINFO *patch;
4968 UINT i;
4970 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4972 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4974 features = msi_split_string( remove, ',' );
4975 for (i = 0; features && features[i]; i++)
4977 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4979 msi_free(features);
4981 if (!full_uninstall)
4982 return ERROR_SUCCESS;
4984 MSIREG_DeleteProductKey(package->ProductCode);
4985 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4986 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4988 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4989 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4990 MSIREG_DeleteUserProductKey(package->ProductCode);
4991 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4993 upgrade = msi_dup_property(package->db, szUpgradeCode);
4994 if (upgrade)
4996 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4997 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4998 msi_free(upgrade);
5001 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5003 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5004 if (!strcmpW( package->ProductCode, patch->products ))
5006 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5007 patch->delete_on_close = TRUE;
5009 /* FIXME: remove local patch package if this is the last product */
5011 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5012 package->delete_on_close = TRUE;
5014 msi_unpublish_icons( package );
5015 return ERROR_SUCCESS;
5018 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5020 UINT rc;
5021 WCHAR *remove;
5023 /* turn off scheduling */
5024 package->script->CurrentlyScripting= FALSE;
5026 /* first do the same as an InstallExecute */
5027 rc = ACTION_InstallExecute(package);
5028 if (rc != ERROR_SUCCESS)
5029 return rc;
5031 /* then handle commit actions */
5032 rc = execute_script(package, SCRIPT_COMMIT);
5033 if (rc != ERROR_SUCCESS)
5034 return rc;
5036 remove = msi_dup_property(package->db, szRemove);
5037 rc = msi_unpublish_product(package, remove);
5038 msi_free(remove);
5039 return rc;
5042 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5044 static const WCHAR RunOnce[] = {
5045 'S','o','f','t','w','a','r','e','\\',
5046 'M','i','c','r','o','s','o','f','t','\\',
5047 'W','i','n','d','o','w','s','\\',
5048 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5049 'R','u','n','O','n','c','e',0};
5050 static const WCHAR InstallRunOnce[] = {
5051 'S','o','f','t','w','a','r','e','\\',
5052 'M','i','c','r','o','s','o','f','t','\\',
5053 'W','i','n','d','o','w','s','\\',
5054 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5055 'I','n','s','t','a','l','l','e','r','\\',
5056 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5058 static const WCHAR msiexec_fmt[] = {
5059 '%','s',
5060 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5061 '\"','%','s','\"',0};
5062 static const WCHAR install_fmt[] = {
5063 '/','I',' ','\"','%','s','\"',' ',
5064 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5065 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5066 WCHAR buffer[256], sysdir[MAX_PATH];
5067 HKEY hkey;
5068 WCHAR squished_pc[100];
5070 squash_guid(package->ProductCode,squished_pc);
5072 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5073 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5074 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5075 squished_pc);
5077 msi_reg_set_val_str( hkey, squished_pc, buffer );
5078 RegCloseKey(hkey);
5080 TRACE("Reboot command %s\n",debugstr_w(buffer));
5082 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5083 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5085 msi_reg_set_val_str( hkey, squished_pc, buffer );
5086 RegCloseKey(hkey);
5088 return ERROR_INSTALL_SUSPEND;
5091 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5093 static const WCHAR query[] =
5094 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5095 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5096 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5097 MSIRECORD *rec, *row;
5098 DWORD i, size = 0;
5099 va_list va;
5100 const WCHAR *str;
5101 WCHAR *data;
5103 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5105 rec = MSI_CreateRecord( count + 2 );
5106 str = MSI_RecordGetString( row, 1 );
5107 MSI_RecordSetStringW( rec, 0, str );
5108 msiobj_release( &row->hdr );
5109 MSI_RecordSetInteger( rec, 1, error );
5111 va_start( va, count );
5112 for (i = 0; i < count; i++)
5114 str = va_arg( va, const WCHAR *);
5115 MSI_RecordSetStringW( rec, i + 2, str );
5117 va_end( va );
5119 MSI_FormatRecordW( package, rec, NULL, &size );
5120 size++;
5121 data = msi_alloc( size * sizeof(WCHAR) );
5122 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5123 else data[0] = 0;
5124 msiobj_release( &rec->hdr );
5125 return data;
5128 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5130 DWORD attrib;
5131 UINT rc;
5134 * We are currently doing what should be done here in the top level Install
5135 * however for Administrative and uninstalls this step will be needed
5137 if (!package->PackagePath)
5138 return ERROR_SUCCESS;
5140 msi_set_sourcedir_props(package, TRUE);
5142 attrib = GetFileAttributesW(package->db->path);
5143 if (attrib == INVALID_FILE_ATTRIBUTES)
5145 LPWSTR prompt, msg;
5146 DWORD size = 0;
5148 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5149 package->Context, MSICODE_PRODUCT,
5150 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5151 if (rc == ERROR_MORE_DATA)
5153 prompt = msi_alloc(size * sizeof(WCHAR));
5154 MsiSourceListGetInfoW(package->ProductCode, NULL,
5155 package->Context, MSICODE_PRODUCT,
5156 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5158 else
5159 prompt = strdupW(package->db->path);
5161 msg = msi_build_error_string(package, 1302, 1, prompt);
5162 msi_free(prompt);
5163 while(attrib == INVALID_FILE_ATTRIBUTES)
5165 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5166 if (rc == IDCANCEL)
5168 msi_free(msg);
5169 return ERROR_INSTALL_USEREXIT;
5171 attrib = GetFileAttributesW(package->db->path);
5173 msi_free(msg);
5174 rc = ERROR_SUCCESS;
5176 else
5177 return ERROR_SUCCESS;
5179 return rc;
5182 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5184 HKEY hkey = 0;
5185 LPWSTR buffer, productid = NULL;
5186 UINT i, rc = ERROR_SUCCESS;
5187 MSIRECORD *uirow;
5189 static const WCHAR szPropKeys[][80] =
5191 {'P','r','o','d','u','c','t','I','D',0},
5192 {'U','S','E','R','N','A','M','E',0},
5193 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5194 {0},
5197 static const WCHAR szRegKeys[][80] =
5199 {'P','r','o','d','u','c','t','I','D',0},
5200 {'R','e','g','O','w','n','e','r',0},
5201 {'R','e','g','C','o','m','p','a','n','y',0},
5202 {0},
5205 if (msi_check_unpublish(package))
5207 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5208 goto end;
5211 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5212 if (!productid)
5213 goto end;
5215 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5216 NULL, &hkey, TRUE);
5217 if (rc != ERROR_SUCCESS)
5218 goto end;
5220 for( i = 0; szPropKeys[i][0]; i++ )
5222 buffer = msi_dup_property( package->db, szPropKeys[i] );
5223 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5224 msi_free( buffer );
5227 end:
5228 uirow = MSI_CreateRecord( 1 );
5229 MSI_RecordSetStringW( uirow, 1, productid );
5230 msi_ui_actiondata( package, szRegisterUser, uirow );
5231 msiobj_release( &uirow->hdr );
5233 msi_free(productid);
5234 RegCloseKey(hkey);
5235 return rc;
5239 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5241 UINT rc;
5243 package->script->InWhatSequence |= SEQUENCE_EXEC;
5244 rc = ACTION_ProcessExecSequence(package,FALSE);
5245 return rc;
5248 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5250 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5251 WCHAR productid_85[21], component_85[21], *ret;
5252 GUID clsid;
5253 DWORD sz;
5255 /* > is used if there is a component GUID and < if not. */
5257 productid_85[0] = 0;
5258 component_85[0] = 0;
5259 CLSIDFromString( package->ProductCode, &clsid );
5261 encode_base85_guid( &clsid, productid_85 );
5262 if (component)
5264 CLSIDFromString( component->ComponentId, &clsid );
5265 encode_base85_guid( &clsid, component_85 );
5268 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5269 debugstr_w(component_85));
5271 sz = 20 + strlenW( feature ) + 20 + 3;
5272 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5273 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5274 return ret;
5277 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5279 MSIPACKAGE *package = param;
5280 LPCWSTR compgroupid, component, feature, qualifier, text;
5281 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5282 HKEY hkey = NULL;
5283 UINT rc;
5284 MSICOMPONENT *comp;
5285 MSIFEATURE *feat;
5286 DWORD sz;
5287 MSIRECORD *uirow;
5288 int len;
5290 feature = MSI_RecordGetString(rec, 5);
5291 feat = msi_get_loaded_feature(package, feature);
5292 if (!feat)
5293 return ERROR_SUCCESS;
5295 feat->Action = msi_get_feature_action( package, feat );
5296 if (feat->Action != INSTALLSTATE_LOCAL &&
5297 feat->Action != INSTALLSTATE_SOURCE &&
5298 feat->Action != INSTALLSTATE_ADVERTISED)
5300 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5301 return ERROR_SUCCESS;
5304 component = MSI_RecordGetString(rec, 3);
5305 comp = msi_get_loaded_component(package, component);
5306 if (!comp)
5307 return ERROR_SUCCESS;
5309 compgroupid = MSI_RecordGetString(rec,1);
5310 qualifier = MSI_RecordGetString(rec,2);
5312 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5313 if (rc != ERROR_SUCCESS)
5314 goto end;
5316 advertise = msi_create_component_advertise_string( package, comp, feature );
5317 text = MSI_RecordGetString( rec, 4 );
5318 if (text)
5320 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5321 strcpyW( p, advertise );
5322 strcatW( p, text );
5323 msi_free( advertise );
5324 advertise = p;
5326 existing = msi_reg_get_val_str( hkey, qualifier );
5328 sz = strlenW( advertise ) + 1;
5329 if (existing)
5331 for (p = existing; *p; p += len)
5333 len = strlenW( p ) + 1;
5334 if (strcmpW( advertise, p )) sz += len;
5337 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5339 rc = ERROR_OUTOFMEMORY;
5340 goto end;
5342 q = output;
5343 if (existing)
5345 for (p = existing; *p; p += len)
5347 len = strlenW( p ) + 1;
5348 if (strcmpW( advertise, p ))
5350 memcpy( q, p, len * sizeof(WCHAR) );
5351 q += len;
5355 strcpyW( q, advertise );
5356 q[strlenW( q ) + 1] = 0;
5358 msi_reg_set_val_multi_str( hkey, qualifier, output );
5360 end:
5361 RegCloseKey(hkey);
5362 msi_free( output );
5363 msi_free( advertise );
5364 msi_free( existing );
5366 /* the UI chunk */
5367 uirow = MSI_CreateRecord( 2 );
5368 MSI_RecordSetStringW( uirow, 1, compgroupid );
5369 MSI_RecordSetStringW( uirow, 2, qualifier);
5370 msi_ui_actiondata( package, szPublishComponents, uirow );
5371 msiobj_release( &uirow->hdr );
5372 /* FIXME: call ui_progress? */
5374 return rc;
5378 * At present I am ignorning the advertised components part of this and only
5379 * focusing on the qualified component sets
5381 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5383 static const WCHAR query[] = {
5384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5385 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5386 MSIQUERY *view;
5387 UINT rc;
5389 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5390 if (rc != ERROR_SUCCESS)
5391 return ERROR_SUCCESS;
5393 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5394 msiobj_release(&view->hdr);
5395 return rc;
5398 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5400 static const WCHAR szInstallerComponents[] = {
5401 'S','o','f','t','w','a','r','e','\\',
5402 'M','i','c','r','o','s','o','f','t','\\',
5403 'I','n','s','t','a','l','l','e','r','\\',
5404 'C','o','m','p','o','n','e','n','t','s','\\',0};
5406 MSIPACKAGE *package = param;
5407 LPCWSTR compgroupid, component, feature, qualifier;
5408 MSICOMPONENT *comp;
5409 MSIFEATURE *feat;
5410 MSIRECORD *uirow;
5411 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5412 LONG res;
5414 feature = MSI_RecordGetString( rec, 5 );
5415 feat = msi_get_loaded_feature( package, feature );
5416 if (!feat)
5417 return ERROR_SUCCESS;
5419 feat->Action = msi_get_feature_action( package, feat );
5420 if (feat->Action != INSTALLSTATE_ABSENT)
5422 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5423 return ERROR_SUCCESS;
5426 component = MSI_RecordGetString( rec, 3 );
5427 comp = msi_get_loaded_component( package, component );
5428 if (!comp)
5429 return ERROR_SUCCESS;
5431 compgroupid = MSI_RecordGetString( rec, 1 );
5432 qualifier = MSI_RecordGetString( rec, 2 );
5434 squash_guid( compgroupid, squashed );
5435 strcpyW( keypath, szInstallerComponents );
5436 strcatW( keypath, squashed );
5438 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5439 if (res != ERROR_SUCCESS)
5441 WARN("Unable to delete component key %d\n", res);
5444 uirow = MSI_CreateRecord( 2 );
5445 MSI_RecordSetStringW( uirow, 1, compgroupid );
5446 MSI_RecordSetStringW( uirow, 2, qualifier );
5447 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5448 msiobj_release( &uirow->hdr );
5450 return ERROR_SUCCESS;
5453 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5455 static const WCHAR query[] = {
5456 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5457 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5458 MSIQUERY *view;
5459 UINT rc;
5461 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5462 if (rc != ERROR_SUCCESS)
5463 return ERROR_SUCCESS;
5465 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5466 msiobj_release( &view->hdr );
5467 return rc;
5470 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5472 static const WCHAR query[] =
5473 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5474 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5475 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5476 MSIPACKAGE *package = param;
5477 MSICOMPONENT *component;
5478 MSIRECORD *row;
5479 MSIFILE *file;
5480 SC_HANDLE hscm = NULL, service = NULL;
5481 LPCWSTR comp, key;
5482 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5483 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5484 DWORD serv_type, start_type, err_control;
5485 SERVICE_DESCRIPTIONW sd = {NULL};
5487 comp = MSI_RecordGetString( rec, 12 );
5488 component = msi_get_loaded_component( package, comp );
5489 if (!component)
5491 WARN("service component not found\n");
5492 goto done;
5494 component->Action = msi_get_component_action( package, component );
5495 if (component->Action != INSTALLSTATE_LOCAL)
5497 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5498 goto done;
5500 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5501 if (!hscm)
5503 ERR("Failed to open the SC Manager!\n");
5504 goto done;
5507 start_type = MSI_RecordGetInteger(rec, 5);
5508 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5509 goto done;
5511 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5512 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5513 serv_type = MSI_RecordGetInteger(rec, 4);
5514 err_control = MSI_RecordGetInteger(rec, 6);
5515 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5516 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5517 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5518 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5519 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5520 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5522 /* fetch the service path */
5523 row = MSI_QueryGetRecord(package->db, query, comp);
5524 if (!row)
5526 ERR("Query failed\n");
5527 goto done;
5529 key = MSI_RecordGetString(row, 6);
5530 file = msi_get_loaded_file(package, key);
5531 msiobj_release(&row->hdr);
5532 if (!file)
5534 ERR("Failed to load the service file\n");
5535 goto done;
5538 if (!args || !args[0]) image_path = file->TargetPath;
5539 else
5541 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5542 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5543 return ERROR_OUTOFMEMORY;
5545 strcpyW(image_path, file->TargetPath);
5546 strcatW(image_path, szSpace);
5547 strcatW(image_path, args);
5549 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5550 start_type, err_control, image_path, load_order,
5551 NULL, depends, serv_name, pass);
5553 if (!service)
5555 if (GetLastError() != ERROR_SERVICE_EXISTS)
5556 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5558 else if (sd.lpDescription)
5560 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5561 WARN("failed to set service description %u\n", GetLastError());
5564 if (image_path != file->TargetPath) msi_free(image_path);
5565 done:
5566 CloseServiceHandle(service);
5567 CloseServiceHandle(hscm);
5568 msi_free(name);
5569 msi_free(disp);
5570 msi_free(sd.lpDescription);
5571 msi_free(load_order);
5572 msi_free(serv_name);
5573 msi_free(pass);
5574 msi_free(depends);
5575 msi_free(args);
5577 return ERROR_SUCCESS;
5580 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5582 static const WCHAR query[] = {
5583 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5584 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5585 MSIQUERY *view;
5586 UINT rc;
5588 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5589 if (rc != ERROR_SUCCESS)
5590 return ERROR_SUCCESS;
5592 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5593 msiobj_release(&view->hdr);
5594 return rc;
5597 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5598 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5600 LPCWSTR *vector, *temp_vector;
5601 LPWSTR p, q;
5602 DWORD sep_len;
5604 static const WCHAR separator[] = {'[','~',']',0};
5606 *numargs = 0;
5607 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5609 if (!args)
5610 return NULL;
5612 vector = msi_alloc(sizeof(LPWSTR));
5613 if (!vector)
5614 return NULL;
5616 p = args;
5619 (*numargs)++;
5620 vector[*numargs - 1] = p;
5622 if ((q = strstrW(p, separator)))
5624 *q = '\0';
5626 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5627 if (!temp_vector)
5629 msi_free(vector);
5630 return NULL;
5632 vector = temp_vector;
5634 p = q + sep_len;
5636 } while (q);
5638 return vector;
5641 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5643 MSIPACKAGE *package = param;
5644 MSICOMPONENT *comp;
5645 MSIRECORD *uirow;
5646 SC_HANDLE scm = NULL, service = NULL;
5647 LPCWSTR component, *vector = NULL;
5648 LPWSTR name, args, display_name = NULL;
5649 DWORD event, numargs, len, wait, dummy;
5650 UINT r = ERROR_FUNCTION_FAILED;
5651 SERVICE_STATUS_PROCESS status;
5652 ULONGLONG start_time;
5654 component = MSI_RecordGetString(rec, 6);
5655 comp = msi_get_loaded_component(package, component);
5656 if (!comp)
5657 return ERROR_SUCCESS;
5659 comp->Action = msi_get_component_action( package, comp );
5660 if (comp->Action != INSTALLSTATE_LOCAL)
5662 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5663 return ERROR_SUCCESS;
5666 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5667 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5668 event = MSI_RecordGetInteger(rec, 3);
5669 wait = MSI_RecordGetInteger(rec, 5);
5671 if (!(event & msidbServiceControlEventStart))
5673 r = ERROR_SUCCESS;
5674 goto done;
5677 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5678 if (!scm)
5680 ERR("Failed to open the service control manager\n");
5681 goto done;
5684 len = 0;
5685 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5686 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5688 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5689 GetServiceDisplayNameW( scm, name, display_name, &len );
5692 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5693 if (!service)
5695 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5696 goto done;
5699 vector = msi_service_args_to_vector(args, &numargs);
5701 if (!StartServiceW(service, numargs, vector) &&
5702 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5704 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5705 goto done;
5708 r = ERROR_SUCCESS;
5709 if (wait)
5711 /* wait for at most 30 seconds for the service to be up and running */
5712 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5713 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5715 TRACE("failed to query service status (%u)\n", GetLastError());
5716 goto done;
5718 start_time = GetTickCount64();
5719 while (status.dwCurrentState == SERVICE_START_PENDING)
5721 if (GetTickCount64() - start_time > 30000) break;
5722 Sleep(1000);
5723 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5724 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5726 TRACE("failed to query service status (%u)\n", GetLastError());
5727 goto done;
5730 if (status.dwCurrentState != SERVICE_RUNNING)
5732 WARN("service failed to start %u\n", status.dwCurrentState);
5733 r = ERROR_FUNCTION_FAILED;
5737 done:
5738 uirow = MSI_CreateRecord( 2 );
5739 MSI_RecordSetStringW( uirow, 1, display_name );
5740 MSI_RecordSetStringW( uirow, 2, name );
5741 msi_ui_actiondata( package, szStartServices, uirow );
5742 msiobj_release( &uirow->hdr );
5744 CloseServiceHandle(service);
5745 CloseServiceHandle(scm);
5747 msi_free(name);
5748 msi_free(args);
5749 msi_free(vector);
5750 msi_free(display_name);
5751 return r;
5754 static UINT ACTION_StartServices( MSIPACKAGE *package )
5756 static const WCHAR query[] = {
5757 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5758 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5759 MSIQUERY *view;
5760 UINT rc;
5762 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5763 if (rc != ERROR_SUCCESS)
5764 return ERROR_SUCCESS;
5766 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5767 msiobj_release(&view->hdr);
5768 return rc;
5771 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5773 DWORD i, needed, count;
5774 ENUM_SERVICE_STATUSW *dependencies;
5775 SERVICE_STATUS ss;
5776 SC_HANDLE depserv;
5778 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5779 0, &needed, &count))
5780 return TRUE;
5782 if (GetLastError() != ERROR_MORE_DATA)
5783 return FALSE;
5785 dependencies = msi_alloc(needed);
5786 if (!dependencies)
5787 return FALSE;
5789 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5790 needed, &needed, &count))
5791 goto error;
5793 for (i = 0; i < count; i++)
5795 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5796 SERVICE_STOP | SERVICE_QUERY_STATUS);
5797 if (!depserv)
5798 goto error;
5800 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5801 goto error;
5804 return TRUE;
5806 error:
5807 msi_free(dependencies);
5808 return FALSE;
5811 static UINT stop_service( LPCWSTR name )
5813 SC_HANDLE scm = NULL, service = NULL;
5814 SERVICE_STATUS status;
5815 SERVICE_STATUS_PROCESS ssp;
5816 DWORD needed;
5818 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5819 if (!scm)
5821 WARN("Failed to open the SCM: %d\n", GetLastError());
5822 goto done;
5825 service = OpenServiceW(scm, name,
5826 SERVICE_STOP |
5827 SERVICE_QUERY_STATUS |
5828 SERVICE_ENUMERATE_DEPENDENTS);
5829 if (!service)
5831 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5832 goto done;
5835 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5836 sizeof(SERVICE_STATUS_PROCESS), &needed))
5838 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5839 goto done;
5842 if (ssp.dwCurrentState == SERVICE_STOPPED)
5843 goto done;
5845 stop_service_dependents(scm, service);
5847 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5848 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5850 done:
5851 CloseServiceHandle(service);
5852 CloseServiceHandle(scm);
5854 return ERROR_SUCCESS;
5857 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5859 MSIPACKAGE *package = param;
5860 MSICOMPONENT *comp;
5861 MSIRECORD *uirow;
5862 LPCWSTR component;
5863 LPWSTR name = NULL, display_name = NULL;
5864 DWORD event, len;
5865 SC_HANDLE scm;
5867 event = MSI_RecordGetInteger( rec, 3 );
5868 if (!(event & msidbServiceControlEventStop))
5869 return ERROR_SUCCESS;
5871 component = MSI_RecordGetString( rec, 6 );
5872 comp = msi_get_loaded_component( package, component );
5873 if (!comp)
5874 return ERROR_SUCCESS;
5876 comp->Action = msi_get_component_action( package, comp );
5877 if (comp->Action != INSTALLSTATE_ABSENT)
5879 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5880 return ERROR_SUCCESS;
5883 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5884 if (!scm)
5886 ERR("Failed to open the service control manager\n");
5887 goto done;
5890 len = 0;
5891 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5892 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5894 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5895 GetServiceDisplayNameW( scm, name, display_name, &len );
5897 CloseServiceHandle( scm );
5899 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5900 stop_service( name );
5902 done:
5903 uirow = MSI_CreateRecord( 2 );
5904 MSI_RecordSetStringW( uirow, 1, display_name );
5905 MSI_RecordSetStringW( uirow, 2, name );
5906 msi_ui_actiondata( package, szStopServices, uirow );
5907 msiobj_release( &uirow->hdr );
5909 msi_free( name );
5910 msi_free( display_name );
5911 return ERROR_SUCCESS;
5914 static UINT ACTION_StopServices( MSIPACKAGE *package )
5916 static const WCHAR query[] = {
5917 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5918 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5919 MSIQUERY *view;
5920 UINT rc;
5922 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5923 if (rc != ERROR_SUCCESS)
5924 return ERROR_SUCCESS;
5926 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5927 msiobj_release(&view->hdr);
5928 return rc;
5931 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5933 MSIPACKAGE *package = param;
5934 MSICOMPONENT *comp;
5935 MSIRECORD *uirow;
5936 LPCWSTR component;
5937 LPWSTR name = NULL, display_name = NULL;
5938 DWORD event, len;
5939 SC_HANDLE scm = NULL, service = NULL;
5941 event = MSI_RecordGetInteger( rec, 3 );
5942 if (!(event & msidbServiceControlEventDelete))
5943 return ERROR_SUCCESS;
5945 component = MSI_RecordGetString(rec, 6);
5946 comp = msi_get_loaded_component(package, component);
5947 if (!comp)
5948 return ERROR_SUCCESS;
5950 comp->Action = msi_get_component_action( package, comp );
5951 if (comp->Action != INSTALLSTATE_ABSENT)
5953 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5954 return ERROR_SUCCESS;
5957 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5958 stop_service( name );
5960 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5961 if (!scm)
5963 WARN("Failed to open the SCM: %d\n", GetLastError());
5964 goto done;
5967 len = 0;
5968 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5969 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5971 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5972 GetServiceDisplayNameW( scm, name, display_name, &len );
5975 service = OpenServiceW( scm, name, DELETE );
5976 if (!service)
5978 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5979 goto done;
5982 if (!DeleteService( service ))
5983 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5985 done:
5986 uirow = MSI_CreateRecord( 2 );
5987 MSI_RecordSetStringW( uirow, 1, display_name );
5988 MSI_RecordSetStringW( uirow, 2, name );
5989 msi_ui_actiondata( package, szDeleteServices, uirow );
5990 msiobj_release( &uirow->hdr );
5992 CloseServiceHandle( service );
5993 CloseServiceHandle( scm );
5994 msi_free( name );
5995 msi_free( display_name );
5997 return ERROR_SUCCESS;
6000 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6002 static const WCHAR query[] = {
6003 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6004 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6005 MSIQUERY *view;
6006 UINT rc;
6008 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6009 if (rc != ERROR_SUCCESS)
6010 return ERROR_SUCCESS;
6012 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6013 msiobj_release( &view->hdr );
6014 return rc;
6017 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6019 MSIPACKAGE *package = param;
6020 LPWSTR driver, driver_path, ptr;
6021 WCHAR outpath[MAX_PATH];
6022 MSIFILE *driver_file = NULL, *setup_file = NULL;
6023 MSICOMPONENT *comp;
6024 MSIRECORD *uirow;
6025 LPCWSTR desc, file_key, component;
6026 DWORD len, usage;
6027 UINT r = ERROR_SUCCESS;
6029 static const WCHAR driver_fmt[] = {
6030 'D','r','i','v','e','r','=','%','s',0};
6031 static const WCHAR setup_fmt[] = {
6032 'S','e','t','u','p','=','%','s',0};
6033 static const WCHAR usage_fmt[] = {
6034 'F','i','l','e','U','s','a','g','e','=','1',0};
6036 component = MSI_RecordGetString( rec, 2 );
6037 comp = msi_get_loaded_component( package, component );
6038 if (!comp)
6039 return ERROR_SUCCESS;
6041 comp->Action = msi_get_component_action( package, comp );
6042 if (comp->Action != INSTALLSTATE_LOCAL)
6044 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6045 return ERROR_SUCCESS;
6047 desc = MSI_RecordGetString(rec, 3);
6049 file_key = MSI_RecordGetString( rec, 4 );
6050 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6052 file_key = MSI_RecordGetString( rec, 5 );
6053 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6055 if (!driver_file)
6057 ERR("ODBC Driver entry not found!\n");
6058 return ERROR_FUNCTION_FAILED;
6061 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6062 if (setup_file)
6063 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6064 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6066 driver = msi_alloc(len * sizeof(WCHAR));
6067 if (!driver)
6068 return ERROR_OUTOFMEMORY;
6070 ptr = driver;
6071 lstrcpyW(ptr, desc);
6072 ptr += lstrlenW(ptr) + 1;
6074 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6075 ptr += len + 1;
6077 if (setup_file)
6079 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6080 ptr += len + 1;
6083 lstrcpyW(ptr, usage_fmt);
6084 ptr += lstrlenW(ptr) + 1;
6085 *ptr = '\0';
6087 driver_path = strdupW(driver_file->TargetPath);
6088 ptr = strrchrW(driver_path, '\\');
6089 if (ptr) *ptr = '\0';
6091 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6092 NULL, ODBC_INSTALL_COMPLETE, &usage))
6094 ERR("Failed to install SQL driver!\n");
6095 r = ERROR_FUNCTION_FAILED;
6098 uirow = MSI_CreateRecord( 5 );
6099 MSI_RecordSetStringW( uirow, 1, desc );
6100 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6101 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6102 msi_ui_actiondata( package, szInstallODBC, uirow );
6103 msiobj_release( &uirow->hdr );
6105 msi_free(driver);
6106 msi_free(driver_path);
6108 return r;
6111 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6113 MSIPACKAGE *package = param;
6114 LPWSTR translator, translator_path, ptr;
6115 WCHAR outpath[MAX_PATH];
6116 MSIFILE *translator_file = NULL, *setup_file = NULL;
6117 MSICOMPONENT *comp;
6118 MSIRECORD *uirow;
6119 LPCWSTR desc, file_key, component;
6120 DWORD len, usage;
6121 UINT r = ERROR_SUCCESS;
6123 static const WCHAR translator_fmt[] = {
6124 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6125 static const WCHAR setup_fmt[] = {
6126 'S','e','t','u','p','=','%','s',0};
6128 component = MSI_RecordGetString( rec, 2 );
6129 comp = msi_get_loaded_component( package, component );
6130 if (!comp)
6131 return ERROR_SUCCESS;
6133 comp->Action = msi_get_component_action( package, comp );
6134 if (comp->Action != INSTALLSTATE_LOCAL)
6136 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6137 return ERROR_SUCCESS;
6139 desc = MSI_RecordGetString(rec, 3);
6141 file_key = MSI_RecordGetString( rec, 4 );
6142 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6144 file_key = MSI_RecordGetString( rec, 5 );
6145 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6147 if (!translator_file)
6149 ERR("ODBC Translator entry not found!\n");
6150 return ERROR_FUNCTION_FAILED;
6153 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6154 if (setup_file)
6155 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6157 translator = msi_alloc(len * sizeof(WCHAR));
6158 if (!translator)
6159 return ERROR_OUTOFMEMORY;
6161 ptr = translator;
6162 lstrcpyW(ptr, desc);
6163 ptr += lstrlenW(ptr) + 1;
6165 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6166 ptr += len + 1;
6168 if (setup_file)
6170 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6171 ptr += len + 1;
6173 *ptr = '\0';
6175 translator_path = strdupW(translator_file->TargetPath);
6176 ptr = strrchrW(translator_path, '\\');
6177 if (ptr) *ptr = '\0';
6179 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6180 NULL, ODBC_INSTALL_COMPLETE, &usage))
6182 ERR("Failed to install SQL translator!\n");
6183 r = ERROR_FUNCTION_FAILED;
6186 uirow = MSI_CreateRecord( 5 );
6187 MSI_RecordSetStringW( uirow, 1, desc );
6188 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6189 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6190 msi_ui_actiondata( package, szInstallODBC, uirow );
6191 msiobj_release( &uirow->hdr );
6193 msi_free(translator);
6194 msi_free(translator_path);
6196 return r;
6199 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6201 MSIPACKAGE *package = param;
6202 MSICOMPONENT *comp;
6203 LPWSTR attrs;
6204 LPCWSTR desc, driver, component;
6205 WORD request = ODBC_ADD_SYS_DSN;
6206 INT registration;
6207 DWORD len;
6208 UINT r = ERROR_SUCCESS;
6209 MSIRECORD *uirow;
6211 static const WCHAR attrs_fmt[] = {
6212 'D','S','N','=','%','s',0 };
6214 component = MSI_RecordGetString( rec, 2 );
6215 comp = msi_get_loaded_component( package, component );
6216 if (!comp)
6217 return ERROR_SUCCESS;
6219 comp->Action = msi_get_component_action( package, comp );
6220 if (comp->Action != INSTALLSTATE_LOCAL)
6222 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6223 return ERROR_SUCCESS;
6226 desc = MSI_RecordGetString(rec, 3);
6227 driver = MSI_RecordGetString(rec, 4);
6228 registration = MSI_RecordGetInteger(rec, 5);
6230 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6231 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6233 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6234 attrs = msi_alloc(len * sizeof(WCHAR));
6235 if (!attrs)
6236 return ERROR_OUTOFMEMORY;
6238 len = sprintfW(attrs, attrs_fmt, desc);
6239 attrs[len + 1] = 0;
6241 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6243 ERR("Failed to install SQL data source!\n");
6244 r = ERROR_FUNCTION_FAILED;
6247 uirow = MSI_CreateRecord( 5 );
6248 MSI_RecordSetStringW( uirow, 1, desc );
6249 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6250 MSI_RecordSetInteger( uirow, 3, request );
6251 msi_ui_actiondata( package, szInstallODBC, uirow );
6252 msiobj_release( &uirow->hdr );
6254 msi_free(attrs);
6256 return r;
6259 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6261 static const WCHAR driver_query[] = {
6262 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6263 'O','D','B','C','D','r','i','v','e','r',0};
6264 static const WCHAR translator_query[] = {
6265 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6266 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6267 static const WCHAR source_query[] = {
6268 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6269 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6270 MSIQUERY *view;
6271 UINT rc;
6273 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6274 if (rc == ERROR_SUCCESS)
6276 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6277 msiobj_release(&view->hdr);
6278 if (rc != ERROR_SUCCESS)
6279 return rc;
6281 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6282 if (rc == ERROR_SUCCESS)
6284 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6285 msiobj_release(&view->hdr);
6286 if (rc != ERROR_SUCCESS)
6287 return rc;
6289 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6290 if (rc == ERROR_SUCCESS)
6292 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6293 msiobj_release(&view->hdr);
6294 if (rc != ERROR_SUCCESS)
6295 return rc;
6297 return ERROR_SUCCESS;
6300 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6302 MSIPACKAGE *package = param;
6303 MSICOMPONENT *comp;
6304 MSIRECORD *uirow;
6305 DWORD usage;
6306 LPCWSTR desc, component;
6308 component = MSI_RecordGetString( rec, 2 );
6309 comp = msi_get_loaded_component( package, component );
6310 if (!comp)
6311 return ERROR_SUCCESS;
6313 comp->Action = msi_get_component_action( package, comp );
6314 if (comp->Action != INSTALLSTATE_ABSENT)
6316 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6317 return ERROR_SUCCESS;
6320 desc = MSI_RecordGetString( rec, 3 );
6321 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6323 WARN("Failed to remove ODBC driver\n");
6325 else if (!usage)
6327 FIXME("Usage count reached 0\n");
6330 uirow = MSI_CreateRecord( 2 );
6331 MSI_RecordSetStringW( uirow, 1, desc );
6332 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6333 msi_ui_actiondata( package, szRemoveODBC, uirow );
6334 msiobj_release( &uirow->hdr );
6336 return ERROR_SUCCESS;
6339 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6341 MSIPACKAGE *package = param;
6342 MSICOMPONENT *comp;
6343 MSIRECORD *uirow;
6344 DWORD usage;
6345 LPCWSTR desc, component;
6347 component = MSI_RecordGetString( rec, 2 );
6348 comp = msi_get_loaded_component( package, component );
6349 if (!comp)
6350 return ERROR_SUCCESS;
6352 comp->Action = msi_get_component_action( package, comp );
6353 if (comp->Action != INSTALLSTATE_ABSENT)
6355 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6356 return ERROR_SUCCESS;
6359 desc = MSI_RecordGetString( rec, 3 );
6360 if (!SQLRemoveTranslatorW( desc, &usage ))
6362 WARN("Failed to remove ODBC translator\n");
6364 else if (!usage)
6366 FIXME("Usage count reached 0\n");
6369 uirow = MSI_CreateRecord( 2 );
6370 MSI_RecordSetStringW( uirow, 1, desc );
6371 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6372 msi_ui_actiondata( package, szRemoveODBC, uirow );
6373 msiobj_release( &uirow->hdr );
6375 return ERROR_SUCCESS;
6378 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6380 MSIPACKAGE *package = param;
6381 MSICOMPONENT *comp;
6382 MSIRECORD *uirow;
6383 LPWSTR attrs;
6384 LPCWSTR desc, driver, component;
6385 WORD request = ODBC_REMOVE_SYS_DSN;
6386 INT registration;
6387 DWORD len;
6389 static const WCHAR attrs_fmt[] = {
6390 'D','S','N','=','%','s',0 };
6392 component = MSI_RecordGetString( rec, 2 );
6393 comp = msi_get_loaded_component( package, component );
6394 if (!comp)
6395 return ERROR_SUCCESS;
6397 comp->Action = msi_get_component_action( package, comp );
6398 if (comp->Action != INSTALLSTATE_ABSENT)
6400 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6401 return ERROR_SUCCESS;
6404 desc = MSI_RecordGetString( rec, 3 );
6405 driver = MSI_RecordGetString( rec, 4 );
6406 registration = MSI_RecordGetInteger( rec, 5 );
6408 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6409 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6411 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6412 attrs = msi_alloc( len * sizeof(WCHAR) );
6413 if (!attrs)
6414 return ERROR_OUTOFMEMORY;
6416 FIXME("Use ODBCSourceAttribute table\n");
6418 len = sprintfW( attrs, attrs_fmt, desc );
6419 attrs[len + 1] = 0;
6421 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6423 WARN("Failed to remove ODBC data source\n");
6425 msi_free( attrs );
6427 uirow = MSI_CreateRecord( 3 );
6428 MSI_RecordSetStringW( uirow, 1, desc );
6429 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6430 MSI_RecordSetInteger( uirow, 3, request );
6431 msi_ui_actiondata( package, szRemoveODBC, uirow );
6432 msiobj_release( &uirow->hdr );
6434 return ERROR_SUCCESS;
6437 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6439 static const WCHAR driver_query[] = {
6440 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6441 'O','D','B','C','D','r','i','v','e','r',0};
6442 static const WCHAR translator_query[] = {
6443 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6444 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6445 static const WCHAR source_query[] = {
6446 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6447 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6448 MSIQUERY *view;
6449 UINT rc;
6451 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6452 if (rc == ERROR_SUCCESS)
6454 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6455 msiobj_release( &view->hdr );
6456 if (rc != ERROR_SUCCESS)
6457 return rc;
6459 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6460 if (rc == ERROR_SUCCESS)
6462 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6463 msiobj_release( &view->hdr );
6464 if (rc != ERROR_SUCCESS)
6465 return rc;
6467 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6468 if (rc == ERROR_SUCCESS)
6470 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6471 msiobj_release( &view->hdr );
6472 if (rc != ERROR_SUCCESS)
6473 return rc;
6475 return ERROR_SUCCESS;
6478 #define ENV_ACT_SETALWAYS 0x1
6479 #define ENV_ACT_SETABSENT 0x2
6480 #define ENV_ACT_REMOVE 0x4
6481 #define ENV_ACT_REMOVEMATCH 0x8
6483 #define ENV_MOD_MACHINE 0x20000000
6484 #define ENV_MOD_APPEND 0x40000000
6485 #define ENV_MOD_PREFIX 0x80000000
6486 #define ENV_MOD_MASK 0xC0000000
6488 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6490 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6492 LPCWSTR cptr = *name;
6494 static const WCHAR prefix[] = {'[','~',']',0};
6495 static const int prefix_len = 3;
6497 *flags = 0;
6498 while (*cptr)
6500 if (*cptr == '=')
6501 *flags |= ENV_ACT_SETALWAYS;
6502 else if (*cptr == '+')
6503 *flags |= ENV_ACT_SETABSENT;
6504 else if (*cptr == '-')
6505 *flags |= ENV_ACT_REMOVE;
6506 else if (*cptr == '!')
6507 *flags |= ENV_ACT_REMOVEMATCH;
6508 else if (*cptr == '*')
6509 *flags |= ENV_MOD_MACHINE;
6510 else
6511 break;
6513 cptr++;
6514 (*name)++;
6517 if (!*cptr)
6519 ERR("Missing environment variable\n");
6520 return ERROR_FUNCTION_FAILED;
6523 if (*value)
6525 LPCWSTR ptr = *value;
6526 if (!strncmpW(ptr, prefix, prefix_len))
6528 if (ptr[prefix_len] == szSemiColon[0])
6530 *flags |= ENV_MOD_APPEND;
6531 *value += lstrlenW(prefix);
6533 else
6535 *value = NULL;
6538 else if (lstrlenW(*value) >= prefix_len)
6540 ptr += lstrlenW(ptr) - prefix_len;
6541 if (!strcmpW( ptr, prefix ))
6543 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6545 *flags |= ENV_MOD_PREFIX;
6546 /* the "[~]" will be removed by deformat_string */;
6548 else
6550 *value = NULL;
6556 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6557 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6558 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6559 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6561 ERR("Invalid flags: %08x\n", *flags);
6562 return ERROR_FUNCTION_FAILED;
6565 if (!*flags)
6566 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6568 return ERROR_SUCCESS;
6571 static UINT open_env_key( DWORD flags, HKEY *key )
6573 static const WCHAR user_env[] =
6574 {'E','n','v','i','r','o','n','m','e','n','t',0};
6575 static const WCHAR machine_env[] =
6576 {'S','y','s','t','e','m','\\',
6577 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6578 'C','o','n','t','r','o','l','\\',
6579 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6580 'E','n','v','i','r','o','n','m','e','n','t',0};
6581 const WCHAR *env;
6582 HKEY root;
6583 LONG res;
6585 if (flags & ENV_MOD_MACHINE)
6587 env = machine_env;
6588 root = HKEY_LOCAL_MACHINE;
6590 else
6592 env = user_env;
6593 root = HKEY_CURRENT_USER;
6596 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6597 if (res != ERROR_SUCCESS)
6599 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6600 return ERROR_FUNCTION_FAILED;
6603 return ERROR_SUCCESS;
6606 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6608 MSIPACKAGE *package = param;
6609 LPCWSTR name, value, component;
6610 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6611 DWORD flags, type, size;
6612 UINT res;
6613 HKEY env = NULL;
6614 MSICOMPONENT *comp;
6615 MSIRECORD *uirow;
6616 int action = 0;
6618 component = MSI_RecordGetString(rec, 4);
6619 comp = msi_get_loaded_component(package, component);
6620 if (!comp)
6621 return ERROR_SUCCESS;
6623 comp->Action = msi_get_component_action( package, comp );
6624 if (comp->Action != INSTALLSTATE_LOCAL)
6626 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6627 return ERROR_SUCCESS;
6629 name = MSI_RecordGetString(rec, 2);
6630 value = MSI_RecordGetString(rec, 3);
6632 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6634 res = env_parse_flags(&name, &value, &flags);
6635 if (res != ERROR_SUCCESS || !value)
6636 goto done;
6638 if (value && !deformat_string(package, value, &deformatted))
6640 res = ERROR_OUTOFMEMORY;
6641 goto done;
6644 value = deformatted;
6646 res = open_env_key( flags, &env );
6647 if (res != ERROR_SUCCESS)
6648 goto done;
6650 if (flags & ENV_MOD_MACHINE)
6651 action |= 0x20000000;
6653 size = 0;
6654 type = REG_SZ;
6655 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6656 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6657 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6658 goto done;
6660 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6662 action = 0x2;
6664 /* Nothing to do. */
6665 if (!value)
6667 res = ERROR_SUCCESS;
6668 goto done;
6671 /* If we are appending but the string was empty, strip ; */
6672 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6674 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6675 newval = strdupW(value);
6676 if (!newval)
6678 res = ERROR_OUTOFMEMORY;
6679 goto done;
6682 else
6684 action = 0x1;
6686 /* Contrary to MSDN, +-variable to [~];path works */
6687 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6689 res = ERROR_SUCCESS;
6690 goto done;
6693 data = msi_alloc(size);
6694 if (!data)
6696 RegCloseKey(env);
6697 return ERROR_OUTOFMEMORY;
6700 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6701 if (res != ERROR_SUCCESS)
6702 goto done;
6704 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6706 action = 0x4;
6707 res = RegDeleteValueW(env, name);
6708 if (res != ERROR_SUCCESS)
6709 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6710 goto done;
6713 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6714 if (flags & ENV_MOD_MASK)
6716 DWORD mod_size;
6717 int multiplier = 0;
6718 if (flags & ENV_MOD_APPEND) multiplier++;
6719 if (flags & ENV_MOD_PREFIX) multiplier++;
6720 mod_size = lstrlenW(value) * multiplier;
6721 size += mod_size * sizeof(WCHAR);
6724 newval = msi_alloc(size);
6725 ptr = newval;
6726 if (!newval)
6728 res = ERROR_OUTOFMEMORY;
6729 goto done;
6732 if (flags & ENV_MOD_PREFIX)
6734 lstrcpyW(newval, value);
6735 ptr = newval + lstrlenW(value);
6736 action |= 0x80000000;
6739 lstrcpyW(ptr, data);
6741 if (flags & ENV_MOD_APPEND)
6743 lstrcatW(newval, value);
6744 action |= 0x40000000;
6747 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6748 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6749 if (res)
6751 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6754 done:
6755 uirow = MSI_CreateRecord( 3 );
6756 MSI_RecordSetStringW( uirow, 1, name );
6757 MSI_RecordSetStringW( uirow, 2, newval );
6758 MSI_RecordSetInteger( uirow, 3, action );
6759 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6760 msiobj_release( &uirow->hdr );
6762 if (env) RegCloseKey(env);
6763 msi_free(deformatted);
6764 msi_free(data);
6765 msi_free(newval);
6766 return res;
6769 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6771 static const WCHAR query[] = {
6772 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6773 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6774 MSIQUERY *view;
6775 UINT rc;
6777 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6778 if (rc != ERROR_SUCCESS)
6779 return ERROR_SUCCESS;
6781 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6782 msiobj_release(&view->hdr);
6783 return rc;
6786 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6788 MSIPACKAGE *package = param;
6789 LPCWSTR name, value, component;
6790 LPWSTR deformatted = NULL;
6791 DWORD flags;
6792 HKEY env;
6793 MSICOMPONENT *comp;
6794 MSIRECORD *uirow;
6795 int action = 0;
6796 LONG res;
6797 UINT r;
6799 component = MSI_RecordGetString( rec, 4 );
6800 comp = msi_get_loaded_component( package, component );
6801 if (!comp)
6802 return ERROR_SUCCESS;
6804 comp->Action = msi_get_component_action( package, comp );
6805 if (comp->Action != INSTALLSTATE_ABSENT)
6807 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6808 return ERROR_SUCCESS;
6810 name = MSI_RecordGetString( rec, 2 );
6811 value = MSI_RecordGetString( rec, 3 );
6813 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6815 r = env_parse_flags( &name, &value, &flags );
6816 if (r != ERROR_SUCCESS)
6817 return r;
6819 if (!(flags & ENV_ACT_REMOVE))
6821 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6822 return ERROR_SUCCESS;
6825 if (value && !deformat_string( package, value, &deformatted ))
6826 return ERROR_OUTOFMEMORY;
6828 value = deformatted;
6830 r = open_env_key( flags, &env );
6831 if (r != ERROR_SUCCESS)
6833 r = ERROR_SUCCESS;
6834 goto done;
6837 if (flags & ENV_MOD_MACHINE)
6838 action |= 0x20000000;
6840 TRACE("Removing %s\n", debugstr_w(name));
6842 res = RegDeleteValueW( env, name );
6843 if (res != ERROR_SUCCESS)
6845 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6846 r = ERROR_SUCCESS;
6849 done:
6850 uirow = MSI_CreateRecord( 3 );
6851 MSI_RecordSetStringW( uirow, 1, name );
6852 MSI_RecordSetStringW( uirow, 2, value );
6853 MSI_RecordSetInteger( uirow, 3, action );
6854 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6855 msiobj_release( &uirow->hdr );
6857 if (env) RegCloseKey( env );
6858 msi_free( deformatted );
6859 return r;
6862 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6864 static const WCHAR query[] = {
6865 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6866 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6867 MSIQUERY *view;
6868 UINT rc;
6870 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6871 if (rc != ERROR_SUCCESS)
6872 return ERROR_SUCCESS;
6874 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6875 msiobj_release( &view->hdr );
6876 return rc;
6879 UINT msi_validate_product_id( MSIPACKAGE *package )
6881 LPWSTR key, template, id;
6882 UINT r = ERROR_SUCCESS;
6884 id = msi_dup_property( package->db, szProductID );
6885 if (id)
6887 msi_free( id );
6888 return ERROR_SUCCESS;
6890 template = msi_dup_property( package->db, szPIDTemplate );
6891 key = msi_dup_property( package->db, szPIDKEY );
6892 if (key && template)
6894 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6895 r = msi_set_property( package->db, szProductID, key );
6897 msi_free( template );
6898 msi_free( key );
6899 return r;
6902 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6904 return msi_validate_product_id( package );
6907 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6909 TRACE("\n");
6910 package->need_reboot = 1;
6911 return ERROR_SUCCESS;
6914 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6916 static const WCHAR szAvailableFreeReg[] =
6917 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6918 MSIRECORD *uirow;
6919 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6921 TRACE("%p %d kilobytes\n", package, space);
6923 uirow = MSI_CreateRecord( 1 );
6924 MSI_RecordSetInteger( uirow, 1, space );
6925 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6926 msiobj_release( &uirow->hdr );
6928 return ERROR_SUCCESS;
6931 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6933 TRACE("%p\n", package);
6935 msi_set_property( package->db, szRollbackDisabled, szOne );
6936 return ERROR_SUCCESS;
6939 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6941 FIXME("%p\n", package);
6942 return ERROR_SUCCESS;
6945 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6947 static const WCHAR driver_query[] = {
6948 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6949 'O','D','B','C','D','r','i','v','e','r',0};
6950 static const WCHAR translator_query[] = {
6951 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6952 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6953 MSIQUERY *view;
6954 UINT r, count;
6956 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6957 if (r == ERROR_SUCCESS)
6959 count = 0;
6960 r = MSI_IterateRecords( view, &count, NULL, package );
6961 msiobj_release( &view->hdr );
6962 if (r != ERROR_SUCCESS)
6963 return r;
6964 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6966 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6967 if (r == ERROR_SUCCESS)
6969 count = 0;
6970 r = MSI_IterateRecords( view, &count, NULL, package );
6971 msiobj_release( &view->hdr );
6972 if (r != ERROR_SUCCESS)
6973 return r;
6974 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6976 return ERROR_SUCCESS;
6979 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6981 MSIPACKAGE *package = param;
6982 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6983 WCHAR *value;
6985 if ((value = msi_dup_property( package->db, property )))
6987 FIXME("remove %s\n", debugstr_w(value));
6988 msi_free( value );
6990 return ERROR_SUCCESS;
6993 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6995 static const WCHAR query[] = {
6996 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6997 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6998 MSIQUERY *view;
6999 UINT r;
7001 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7002 if (r == ERROR_SUCCESS)
7004 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7005 msiobj_release( &view->hdr );
7006 if (r != ERROR_SUCCESS)
7007 return r;
7009 return ERROR_SUCCESS;
7012 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7014 MSIPACKAGE *package = param;
7015 int attributes = MSI_RecordGetInteger( rec, 5 );
7017 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7019 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7020 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7021 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7022 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7023 HKEY hkey;
7024 UINT r;
7026 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7028 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7029 if (r != ERROR_SUCCESS)
7030 return ERROR_SUCCESS;
7032 else
7034 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7035 if (r != ERROR_SUCCESS)
7036 return ERROR_SUCCESS;
7038 RegCloseKey( hkey );
7040 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7041 debugstr_w(upgrade_code), debugstr_w(version_min),
7042 debugstr_w(version_max), debugstr_w(language));
7044 return ERROR_SUCCESS;
7047 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7049 static const WCHAR query[] = {
7050 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7051 'U','p','g','r','a','d','e',0};
7052 MSIQUERY *view;
7053 UINT r;
7055 if (msi_get_property_int( package->db, szInstalled, 0 ))
7057 TRACE("product is installed, skipping action\n");
7058 return ERROR_SUCCESS;
7060 if (msi_get_property_int( package->db, szPreselected, 0 ))
7062 TRACE("Preselected property is set, not migrating feature states\n");
7063 return ERROR_SUCCESS;
7065 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7066 if (r == ERROR_SUCCESS)
7068 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7069 msiobj_release( &view->hdr );
7070 if (r != ERROR_SUCCESS)
7071 return r;
7073 return ERROR_SUCCESS;
7076 static void bind_image( const char *filename, const char *path )
7078 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7080 WARN("failed to bind image %u\n", GetLastError());
7084 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7086 UINT i;
7087 MSIFILE *file;
7088 MSIPACKAGE *package = param;
7089 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7090 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7091 char *filenameA, *pathA;
7092 WCHAR *pathW, **path_list;
7094 if (!(file = msi_get_loaded_file( package, key )))
7096 WARN("file %s not found\n", debugstr_w(key));
7097 return ERROR_SUCCESS;
7099 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7100 path_list = msi_split_string( paths, ';' );
7101 if (!path_list) bind_image( filenameA, NULL );
7102 else
7104 for (i = 0; path_list[i] && path_list[i][0]; i++)
7106 deformat_string( package, path_list[i], &pathW );
7107 if ((pathA = strdupWtoA( pathW )))
7109 bind_image( filenameA, pathA );
7110 msi_free( pathA );
7112 msi_free( pathW );
7115 msi_free( path_list );
7116 msi_free( filenameA );
7117 return ERROR_SUCCESS;
7120 static UINT ACTION_BindImage( MSIPACKAGE *package )
7122 static const WCHAR query[] = {
7123 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7124 'B','i','n','d','I','m','a','g','e',0};
7125 MSIQUERY *view;
7126 UINT r;
7128 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7129 if (r == ERROR_SUCCESS)
7131 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7132 msiobj_release( &view->hdr );
7133 if (r != ERROR_SUCCESS)
7134 return r;
7136 return ERROR_SUCCESS;
7139 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7141 static const WCHAR query[] = {
7142 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7143 MSIQUERY *view;
7144 DWORD count = 0;
7145 UINT r;
7147 r = MSI_OpenQuery( package->db, &view, query, table );
7148 if (r == ERROR_SUCCESS)
7150 r = MSI_IterateRecords(view, &count, NULL, package);
7151 msiobj_release(&view->hdr);
7152 if (r != ERROR_SUCCESS)
7153 return r;
7155 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7156 return ERROR_SUCCESS;
7159 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7161 static const WCHAR table[] = {
7162 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7163 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7166 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7168 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7169 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7172 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7174 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7175 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7178 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7180 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7181 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7184 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7186 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7187 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7190 static const struct
7192 const WCHAR *action;
7193 UINT (*handler)(MSIPACKAGE *);
7194 const WCHAR *action_rollback;
7196 StandardActions[] =
7198 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7199 { szAppSearch, ACTION_AppSearch, NULL },
7200 { szBindImage, ACTION_BindImage, NULL },
7201 { szCCPSearch, ACTION_CCPSearch, NULL },
7202 { szCostFinalize, ACTION_CostFinalize, NULL },
7203 { szCostInitialize, ACTION_CostInitialize, NULL },
7204 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7205 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7206 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7207 { szDisableRollback, ACTION_DisableRollback, NULL },
7208 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7209 { szExecuteAction, ACTION_ExecuteAction, NULL },
7210 { szFileCost, ACTION_FileCost, NULL },
7211 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7212 { szForceReboot, ACTION_ForceReboot, NULL },
7213 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7214 { szInstallExecute, ACTION_InstallExecute, NULL },
7215 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7216 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7217 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7218 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7219 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7220 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7221 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7222 { szInstallValidate, ACTION_InstallValidate, NULL },
7223 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7224 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7225 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7226 { szMoveFiles, ACTION_MoveFiles, NULL },
7227 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7228 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7229 { szPatchFiles, ACTION_PatchFiles, NULL },
7230 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7231 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7232 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7233 { szPublishProduct, ACTION_PublishProduct, NULL },
7234 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7235 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7236 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7237 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7238 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7239 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7240 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7241 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7242 { szRegisterUser, ACTION_RegisterUser, NULL },
7243 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7244 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7245 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7246 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7247 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7248 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7249 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7250 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7251 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7252 { szResolveSource, ACTION_ResolveSource, NULL },
7253 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7254 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7255 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7256 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7257 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7258 { szStartServices, ACTION_StartServices, szStopServices },
7259 { szStopServices, ACTION_StopServices, szStartServices },
7260 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7261 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7262 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7263 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7264 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7265 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7266 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7267 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7268 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7269 { szValidateProductID, ACTION_ValidateProductID, NULL },
7270 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7271 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7272 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7273 { NULL, NULL, NULL }
7276 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7278 BOOL ret = FALSE;
7279 UINT i;
7281 i = 0;
7282 while (StandardActions[i].action != NULL)
7284 if (!strcmpW( StandardActions[i].action, action ))
7286 ui_actionstart( package, action );
7287 if (StandardActions[i].handler)
7289 ui_actioninfo( package, action, TRUE, 0 );
7290 *rc = StandardActions[i].handler( package );
7291 ui_actioninfo( package, action, FALSE, *rc );
7293 if (StandardActions[i].action_rollback && !package->need_rollback)
7295 TRACE("scheduling rollback action\n");
7296 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7299 else
7301 FIXME("unhandled standard action %s\n", debugstr_w(action));
7302 *rc = ERROR_SUCCESS;
7304 ret = TRUE;
7305 break;
7307 i++;
7309 return ret;
7312 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7314 UINT rc = ERROR_SUCCESS;
7315 BOOL handled;
7317 TRACE("Performing action (%s)\n", debugstr_w(action));
7319 handled = ACTION_HandleStandardAction(package, action, &rc);
7321 if (!handled)
7322 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7324 if (!handled)
7326 WARN("unhandled msi action %s\n", debugstr_w(action));
7327 rc = ERROR_FUNCTION_NOT_CALLED;
7330 return rc;
7333 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7335 UINT rc = ERROR_SUCCESS;
7336 BOOL handled = FALSE;
7338 TRACE("Performing action (%s)\n", debugstr_w(action));
7340 handled = ACTION_HandleStandardAction(package, action, &rc);
7342 if (!handled)
7343 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7345 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7346 handled = TRUE;
7348 if (!handled)
7350 WARN("unhandled msi action %s\n", debugstr_w(action));
7351 rc = ERROR_FUNCTION_NOT_CALLED;
7354 return rc;
7357 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7359 UINT rc = ERROR_SUCCESS;
7360 MSIRECORD *row;
7362 static const WCHAR query[] =
7363 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7364 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7365 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7366 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7367 static const WCHAR ui_query[] =
7368 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7369 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7370 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7371 ' ', '=',' ','%','i',0};
7373 if (needs_ui_sequence(package))
7374 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7375 else
7376 row = MSI_QueryGetRecord(package->db, query, seq);
7378 if (row)
7380 LPCWSTR action, cond;
7382 TRACE("Running the actions\n");
7384 /* check conditions */
7385 cond = MSI_RecordGetString(row, 2);
7387 /* this is a hack to skip errors in the condition code */
7388 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7390 msiobj_release(&row->hdr);
7391 return ERROR_SUCCESS;
7394 action = MSI_RecordGetString(row, 1);
7395 if (!action)
7397 ERR("failed to fetch action\n");
7398 msiobj_release(&row->hdr);
7399 return ERROR_FUNCTION_FAILED;
7402 if (needs_ui_sequence(package))
7403 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7404 else
7405 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7407 msiobj_release(&row->hdr);
7410 return rc;
7413 /****************************************************
7414 * TOP level entry points
7415 *****************************************************/
7417 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7418 LPCWSTR szCommandLine )
7420 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7421 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7422 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7423 WCHAR *reinstall = NULL;
7424 BOOL ui_exists;
7425 UINT rc;
7427 msi_set_property( package->db, szAction, szInstall );
7429 package->script->InWhatSequence = SEQUENCE_INSTALL;
7431 if (szPackagePath)
7433 LPWSTR p, dir;
7434 LPCWSTR file;
7436 dir = strdupW(szPackagePath);
7437 p = strrchrW(dir, '\\');
7438 if (p)
7440 *(++p) = 0;
7441 file = szPackagePath + (p - dir);
7443 else
7445 msi_free(dir);
7446 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7447 GetCurrentDirectoryW(MAX_PATH, dir);
7448 lstrcatW(dir, szBackSlash);
7449 file = szPackagePath;
7452 msi_free( package->PackagePath );
7453 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7454 if (!package->PackagePath)
7456 msi_free(dir);
7457 return ERROR_OUTOFMEMORY;
7460 lstrcpyW(package->PackagePath, dir);
7461 lstrcatW(package->PackagePath, file);
7462 msi_free(dir);
7464 msi_set_sourcedir_props(package, FALSE);
7467 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7468 if (rc != ERROR_SUCCESS)
7469 return rc;
7471 msi_apply_transforms( package );
7472 msi_apply_patches( package );
7474 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7476 TRACE("setting reinstall property\n");
7477 msi_set_property( package->db, szReinstall, szAll );
7480 /* properties may have been added by a transform */
7481 msi_clone_properties( package );
7483 msi_parse_command_line( package, szCommandLine, FALSE );
7484 msi_adjust_privilege_properties( package );
7485 msi_set_context( package );
7487 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7489 TRACE("disabling rollback\n");
7490 msi_set_property( package->db, szRollbackDisabled, szOne );
7493 if (needs_ui_sequence( package))
7495 package->script->InWhatSequence |= SEQUENCE_UI;
7496 rc = ACTION_ProcessUISequence(package);
7497 ui_exists = ui_sequence_exists(package);
7498 if (rc == ERROR_SUCCESS || !ui_exists)
7500 package->script->InWhatSequence |= SEQUENCE_EXEC;
7501 rc = ACTION_ProcessExecSequence(package, ui_exists);
7504 else
7505 rc = ACTION_ProcessExecSequence(package, FALSE);
7507 package->script->CurrentlyScripting = FALSE;
7509 /* process the ending type action */
7510 if (rc == ERROR_SUCCESS)
7511 ACTION_PerformActionSequence(package, -1);
7512 else if (rc == ERROR_INSTALL_USEREXIT)
7513 ACTION_PerformActionSequence(package, -2);
7514 else if (rc == ERROR_INSTALL_SUSPEND)
7515 ACTION_PerformActionSequence(package, -4);
7516 else /* failed */
7518 ACTION_PerformActionSequence(package, -3);
7519 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7521 package->need_rollback = TRUE;
7525 /* finish up running custom actions */
7526 ACTION_FinishCustomActions(package);
7528 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7530 WARN("installation failed, running rollback script\n");
7531 execute_script( package, SCRIPT_ROLLBACK );
7533 msi_free( reinstall );
7535 if (rc == ERROR_SUCCESS && package->need_reboot)
7536 return ERROR_SUCCESS_REBOOT_REQUIRED;
7538 return rc;