user32: Always use the default alignment when displaying submenu popups.
[wine/multimedia.git] / dlls / msi / action.c
blob77749d89ed2cb706f8b21325f9d03a78bc68631b
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 if (!count) goto done;
226 in_quotes = 1;
227 ignore = 1;
228 len++;
229 break;
230 case '"':
231 state = state_quote;
232 if (in_quotes && p[1] != '\"') count--;
233 else count++;
234 break;
235 default:
236 state = state_token;
237 if (!count) in_quotes = 0;
238 else in_quotes = 1;
239 len++;
240 break;
242 break;
244 case state_token:
245 switch (*p)
247 case '"':
248 state = state_quote;
249 if (in_quotes) count--;
250 else count++;
251 break;
252 case ' ':
253 state = state_whitespace;
254 if (!count) goto done;
255 in_quotes = 1;
256 len++;
257 break;
258 default:
259 if (!count) in_quotes = 0;
260 else in_quotes = 1;
261 len++;
262 break;
264 break;
266 case state_quote:
267 switch (*p)
269 case '"':
270 if (in_quotes && p[1] != '\"') count--;
271 else count++;
272 break;
273 case ' ':
274 state = state_whitespace;
275 if (!count || (count > 1 && !len)) goto done;
276 in_quotes = 1;
277 len++;
278 break;
279 default:
280 state = state_token;
281 if (!count) in_quotes = 0;
282 else in_quotes = 1;
283 len++;
284 break;
286 break;
288 default: break;
290 if (!ignore) *out++ = *p;
293 done:
294 if (!len) *value = 0;
295 else *out = 0;
297 *quotes = count;
298 return p - str;
301 static void remove_quotes( WCHAR *str )
303 WCHAR *p = str;
304 int len = strlenW( str );
306 while ((p = strchrW( p, '"' )))
308 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
309 p++;
313 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
314 BOOL preserve_case )
316 LPCWSTR ptr, ptr2;
317 int num_quotes;
318 DWORD len;
319 WCHAR *prop, *val;
320 UINT r;
322 if (!szCommandLine)
323 return ERROR_SUCCESS;
325 ptr = szCommandLine;
326 while (*ptr)
328 while (*ptr == ' ') ptr++;
329 if (!*ptr) break;
331 ptr2 = strchrW( ptr, '=' );
332 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
334 len = ptr2 - ptr;
335 if (!len) return ERROR_INVALID_COMMAND_LINE;
337 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
338 memcpy( prop, ptr, len * sizeof(WCHAR) );
339 prop[len] = 0;
340 if (!preserve_case) struprW( prop );
342 ptr2++;
343 while (*ptr2 == ' ') ptr2++;
345 num_quotes = 0;
346 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
347 len = parse_prop( ptr2, val, &num_quotes );
348 if (num_quotes % 2)
350 WARN("unbalanced quotes\n");
351 msi_free( val );
352 msi_free( prop );
353 return ERROR_INVALID_COMMAND_LINE;
355 remove_quotes( val );
356 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
358 r = msi_set_property( package->db, prop, val );
359 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
360 msi_reset_folders( package, TRUE );
362 msi_free( val );
363 msi_free( prop );
365 ptr = ptr2 + len;
368 return ERROR_SUCCESS;
371 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
373 LPCWSTR pc;
374 LPWSTR p, *ret = NULL;
375 UINT count = 0;
377 if (!str)
378 return ret;
380 /* count the number of substrings */
381 for ( pc = str, count = 0; pc; count++ )
383 pc = strchrW( pc, sep );
384 if (pc)
385 pc++;
388 /* allocate space for an array of substring pointers and the substrings */
389 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
390 (lstrlenW(str)+1) * sizeof(WCHAR) );
391 if (!ret)
392 return ret;
394 /* copy the string and set the pointers */
395 p = (LPWSTR) &ret[count+1];
396 lstrcpyW( p, str );
397 for( count = 0; (ret[count] = p); count++ )
399 p = strchrW( p, sep );
400 if (p)
401 *p++ = 0;
404 return ret;
407 static BOOL ui_sequence_exists( MSIPACKAGE *package )
409 static const WCHAR query [] = {
410 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
411 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
412 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
413 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
414 MSIQUERY *view;
415 UINT rc;
417 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
418 if (rc == ERROR_SUCCESS)
420 msiobj_release(&view->hdr);
421 return TRUE;
423 return FALSE;
426 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
428 LPWSTR source, check;
430 if (msi_get_property_int( package->db, szInstalled, 0 ))
432 HKEY hkey;
434 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
435 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
436 RegCloseKey( hkey );
438 else
440 LPWSTR p, db;
441 DWORD len;
443 db = msi_dup_property( package->db, szOriginalDatabase );
444 if (!db)
445 return ERROR_OUTOFMEMORY;
447 p = strrchrW( db, '\\' );
448 if (!p)
450 p = strrchrW( db, '/' );
451 if (!p)
453 msi_free(db);
454 return ERROR_SUCCESS;
458 len = p - db + 2;
459 source = msi_alloc( len * sizeof(WCHAR) );
460 lstrcpynW( source, db, len );
461 msi_free( db );
464 check = msi_dup_property( package->db, szSourceDir );
465 if (!check || replace)
467 UINT r = msi_set_property( package->db, szSourceDir, source );
468 if (r == ERROR_SUCCESS)
469 msi_reset_folders( package, TRUE );
471 msi_free( check );
473 check = msi_dup_property( package->db, szSOURCEDIR );
474 if (!check || replace)
475 msi_set_property( package->db, szSOURCEDIR, source );
477 msi_free( check );
478 msi_free( source );
480 return ERROR_SUCCESS;
483 static BOOL needs_ui_sequence(MSIPACKAGE *package)
485 INT level = msi_get_property_int(package->db, szUILevel, 0);
486 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
489 UINT msi_set_context(MSIPACKAGE *package)
491 UINT r = msi_locate_product( package->ProductCode, &package->Context );
492 if (r != ERROR_SUCCESS)
494 int num = msi_get_property_int( package->db, szAllUsers, 0 );
495 if (num == 1 || num == 2)
496 package->Context = MSIINSTALLCONTEXT_MACHINE;
497 else
498 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
500 return ERROR_SUCCESS;
503 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
505 UINT rc;
506 LPCWSTR cond, action;
507 MSIPACKAGE *package = param;
509 action = MSI_RecordGetString(row,1);
510 if (!action)
512 ERR("Error is retrieving action name\n");
513 return ERROR_FUNCTION_FAILED;
516 /* check conditions */
517 cond = MSI_RecordGetString(row,2);
519 /* this is a hack to skip errors in the condition code */
520 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
522 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
523 return ERROR_SUCCESS;
526 if (needs_ui_sequence(package))
527 rc = ACTION_PerformUIAction(package, action, -1);
528 else
529 rc = ACTION_PerformAction(package, action, -1);
531 msi_dialog_check_messages( NULL );
533 if (package->CurrentInstallState != ERROR_SUCCESS)
534 rc = package->CurrentInstallState;
536 if (rc == ERROR_FUNCTION_NOT_CALLED)
537 rc = ERROR_SUCCESS;
539 if (rc != ERROR_SUCCESS)
540 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
542 return rc;
545 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
547 static const WCHAR query[] = {
548 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
549 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
550 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
551 '`','S','e','q','u','e','n','c','e','`',0};
552 MSIQUERY *view;
553 UINT r;
555 TRACE("%p %s\n", package, debugstr_w(table));
557 r = MSI_OpenQuery( package->db, &view, query, table );
558 if (r == ERROR_SUCCESS)
560 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
561 msiobj_release(&view->hdr);
563 return r;
566 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
568 static const WCHAR query[] = {
569 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
570 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
571 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
572 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
573 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
574 static const WCHAR query_validate[] = {
575 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
576 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
577 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
578 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
579 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
580 MSIQUERY *view;
581 INT seq = 0;
582 UINT rc;
584 if (package->script->ExecuteSequenceRun)
586 TRACE("Execute Sequence already Run\n");
587 return ERROR_SUCCESS;
590 package->script->ExecuteSequenceRun = TRUE;
592 /* get the sequence number */
593 if (UIran)
595 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
596 if (!row) return ERROR_FUNCTION_FAILED;
597 seq = MSI_RecordGetInteger(row,1);
598 msiobj_release(&row->hdr);
600 rc = MSI_OpenQuery(package->db, &view, query, seq);
601 if (rc == ERROR_SUCCESS)
603 TRACE("Running the actions\n");
605 msi_set_property(package->db, szSourceDir, NULL);
606 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
607 msiobj_release(&view->hdr);
609 return rc;
612 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
614 static const WCHAR query[] = {
615 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
616 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
617 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
618 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
619 MSIQUERY *view;
620 UINT rc;
622 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
623 if (rc == ERROR_SUCCESS)
625 TRACE("Running the actions\n");
626 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
627 msiobj_release(&view->hdr);
629 return rc;
632 /********************************************************
633 * ACTION helper functions and functions that perform the actions
634 *******************************************************/
635 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
636 UINT* rc, UINT script, BOOL force )
638 BOOL ret=FALSE;
639 UINT arc;
641 arc = ACTION_CustomAction(package, action, script, force);
643 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
645 *rc = arc;
646 ret = TRUE;
648 return ret;
651 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
653 MSICOMPONENT *comp;
655 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
657 if (!strcmpW( Component, comp->Component )) return comp;
659 return NULL;
662 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
664 MSIFEATURE *feature;
666 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
668 if (!strcmpW( Feature, feature->Feature )) return feature;
670 return NULL;
673 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
675 MSIFILE *file;
677 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
679 if (!strcmpW( key, file->File )) return file;
681 return NULL;
684 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
686 MSIFILEPATCH *patch;
688 /* FIXME: There might be more than one patch */
689 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
691 if (!strcmpW( key, patch->File->File )) return patch;
693 return NULL;
696 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
698 MSIFOLDER *folder;
700 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
702 if (!strcmpW( dir, folder->Directory )) return folder;
704 return NULL;
708 * Recursively create all directories in the path.
709 * shamelessly stolen from setupapi/queue.c
711 BOOL msi_create_full_path( const WCHAR *path )
713 BOOL ret = TRUE;
714 WCHAR *new_path;
715 int len;
717 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
718 strcpyW( new_path, path );
720 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
721 new_path[len - 1] = 0;
723 while (!CreateDirectoryW( new_path, NULL ))
725 WCHAR *slash;
726 DWORD last_error = GetLastError();
727 if (last_error == ERROR_ALREADY_EXISTS) break;
728 if (last_error != ERROR_PATH_NOT_FOUND)
730 ret = FALSE;
731 break;
733 if (!(slash = strrchrW( new_path, '\\' )))
735 ret = FALSE;
736 break;
738 len = slash - new_path;
739 new_path[len] = 0;
740 if (!msi_create_full_path( new_path ))
742 ret = FALSE;
743 break;
745 new_path[len] = '\\';
747 msi_free( new_path );
748 return ret;
751 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
753 MSIRECORD *row;
755 row = MSI_CreateRecord( 4 );
756 MSI_RecordSetInteger( row, 1, a );
757 MSI_RecordSetInteger( row, 2, b );
758 MSI_RecordSetInteger( row, 3, c );
759 MSI_RecordSetInteger( row, 4, d );
760 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
761 msiobj_release( &row->hdr );
763 msi_dialog_check_messages( NULL );
766 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
768 static const WCHAR query[] =
769 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
770 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
771 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
772 WCHAR message[1024];
773 MSIRECORD *row = 0;
774 DWORD size;
776 if (!package->LastAction || strcmpW( package->LastAction, action ))
778 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
780 if (MSI_RecordIsNull( row, 3 ))
782 msiobj_release( &row->hdr );
783 return;
785 /* update the cached action format */
786 msi_free( package->ActionFormat );
787 package->ActionFormat = msi_dup_record_field( row, 3 );
788 msi_free( package->LastAction );
789 package->LastAction = strdupW( action );
790 msiobj_release( &row->hdr );
792 size = 1024;
793 MSI_RecordSetStringW( record, 0, package->ActionFormat );
794 MSI_FormatRecordW( package, record, message, &size );
795 row = MSI_CreateRecord( 1 );
796 MSI_RecordSetStringW( row, 1, message );
797 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
798 msiobj_release( &row->hdr );
801 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
803 if (!comp->Enabled)
805 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
806 return INSTALLSTATE_UNKNOWN;
808 if (package->need_rollback) return comp->Installed;
809 return comp->ActionRequest;
812 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
814 if (package->need_rollback) return feature->Installed;
815 return feature->ActionRequest;
818 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
820 MSIPACKAGE *package = param;
821 LPCWSTR dir, component, full_path;
822 MSIRECORD *uirow;
823 MSIFOLDER *folder;
824 MSICOMPONENT *comp;
826 component = MSI_RecordGetString(row, 2);
827 if (!component)
828 return ERROR_SUCCESS;
830 comp = msi_get_loaded_component(package, component);
831 if (!comp)
832 return ERROR_SUCCESS;
834 comp->Action = msi_get_component_action( package, comp );
835 if (comp->Action != INSTALLSTATE_LOCAL)
837 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
838 return ERROR_SUCCESS;
841 dir = MSI_RecordGetString(row,1);
842 if (!dir)
844 ERR("Unable to get folder id\n");
845 return ERROR_SUCCESS;
848 uirow = MSI_CreateRecord(1);
849 MSI_RecordSetStringW(uirow, 1, dir);
850 msi_ui_actiondata(package, szCreateFolders, uirow);
851 msiobj_release(&uirow->hdr);
853 full_path = msi_get_target_folder( package, dir );
854 if (!full_path)
856 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
857 return ERROR_SUCCESS;
859 TRACE("folder is %s\n", debugstr_w(full_path));
861 folder = msi_get_loaded_folder( package, dir );
862 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
863 folder->State = FOLDER_STATE_CREATED;
864 return ERROR_SUCCESS;
867 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
869 static const WCHAR query[] = {
870 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
871 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
872 MSIQUERY *view;
873 UINT rc;
875 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
876 if (rc != ERROR_SUCCESS)
877 return ERROR_SUCCESS;
879 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
880 msiobj_release(&view->hdr);
881 return rc;
884 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
886 MSIPACKAGE *package = param;
887 LPCWSTR dir, component, full_path;
888 MSIRECORD *uirow;
889 MSIFOLDER *folder;
890 MSICOMPONENT *comp;
892 component = MSI_RecordGetString(row, 2);
893 if (!component)
894 return ERROR_SUCCESS;
896 comp = msi_get_loaded_component(package, component);
897 if (!comp)
898 return ERROR_SUCCESS;
900 comp->Action = msi_get_component_action( package, comp );
901 if (comp->Action != INSTALLSTATE_ABSENT)
903 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
904 return ERROR_SUCCESS;
907 dir = MSI_RecordGetString( row, 1 );
908 if (!dir)
910 ERR("Unable to get folder id\n");
911 return ERROR_SUCCESS;
914 full_path = msi_get_target_folder( package, dir );
915 if (!full_path)
917 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
918 return ERROR_SUCCESS;
920 TRACE("folder is %s\n", debugstr_w(full_path));
922 uirow = MSI_CreateRecord( 1 );
923 MSI_RecordSetStringW( uirow, 1, dir );
924 msi_ui_actiondata( package, szRemoveFolders, uirow );
925 msiobj_release( &uirow->hdr );
927 RemoveDirectoryW( full_path );
928 folder = msi_get_loaded_folder( package, dir );
929 folder->State = FOLDER_STATE_REMOVED;
930 return ERROR_SUCCESS;
933 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
935 static const WCHAR query[] = {
936 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
937 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
938 MSIQUERY *view;
939 UINT rc;
941 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
942 if (rc != ERROR_SUCCESS)
943 return ERROR_SUCCESS;
945 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
946 msiobj_release( &view->hdr );
947 return rc;
950 static UINT load_component( MSIRECORD *row, LPVOID param )
952 MSIPACKAGE *package = param;
953 MSICOMPONENT *comp;
955 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
956 if (!comp)
957 return ERROR_FUNCTION_FAILED;
959 list_add_tail( &package->components, &comp->entry );
961 /* fill in the data */
962 comp->Component = msi_dup_record_field( row, 1 );
964 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
966 comp->ComponentId = msi_dup_record_field( row, 2 );
967 comp->Directory = msi_dup_record_field( row, 3 );
968 comp->Attributes = MSI_RecordGetInteger(row,4);
969 comp->Condition = msi_dup_record_field( row, 5 );
970 comp->KeyPath = msi_dup_record_field( row, 6 );
972 comp->Installed = INSTALLSTATE_UNKNOWN;
973 comp->Action = INSTALLSTATE_UNKNOWN;
974 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
976 comp->assembly = msi_load_assembly( package, comp );
977 return ERROR_SUCCESS;
980 UINT msi_load_all_components( MSIPACKAGE *package )
982 static const WCHAR query[] = {
983 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
984 '`','C','o','m','p','o','n','e','n','t','`',0};
985 MSIQUERY *view;
986 UINT r;
988 if (!list_empty(&package->components))
989 return ERROR_SUCCESS;
991 r = MSI_DatabaseOpenViewW( package->db, query, &view );
992 if (r != ERROR_SUCCESS)
993 return r;
995 if (!msi_init_assembly_caches( package ))
997 ERR("can't initialize assembly caches\n");
998 msiobj_release( &view->hdr );
999 return ERROR_FUNCTION_FAILED;
1002 r = MSI_IterateRecords(view, NULL, load_component, package);
1003 msiobj_release(&view->hdr);
1004 msi_destroy_assembly_caches( package );
1005 return r;
1008 typedef struct {
1009 MSIPACKAGE *package;
1010 MSIFEATURE *feature;
1011 } _ilfs;
1013 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1015 ComponentList *cl;
1017 cl = msi_alloc( sizeof (*cl) );
1018 if ( !cl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 cl->component = comp;
1021 list_add_tail( &feature->Components, &cl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1028 FeatureList *fl;
1030 fl = msi_alloc( sizeof(*fl) );
1031 if ( !fl )
1032 return ERROR_NOT_ENOUGH_MEMORY;
1033 fl->feature = child;
1034 list_add_tail( &parent->Children, &fl->entry );
1036 return ERROR_SUCCESS;
1039 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1041 _ilfs* ilfs = param;
1042 LPCWSTR component;
1043 MSICOMPONENT *comp;
1045 component = MSI_RecordGetString(row,1);
1047 /* check to see if the component is already loaded */
1048 comp = msi_get_loaded_component( ilfs->package, component );
1049 if (!comp)
1051 WARN("ignoring unknown component %s\n", debugstr_w(component));
1052 return ERROR_SUCCESS;
1054 add_feature_component( ilfs->feature, comp );
1055 comp->Enabled = TRUE;
1057 return ERROR_SUCCESS;
1060 static UINT load_feature(MSIRECORD * row, LPVOID param)
1062 static const WCHAR query[] = {
1063 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1064 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1065 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1066 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1067 MSIPACKAGE *package = param;
1068 MSIFEATURE *feature;
1069 MSIQUERY *view;
1070 _ilfs ilfs;
1071 UINT rc;
1073 /* fill in the data */
1075 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1076 if (!feature)
1077 return ERROR_NOT_ENOUGH_MEMORY;
1079 list_init( &feature->Children );
1080 list_init( &feature->Components );
1082 feature->Feature = msi_dup_record_field( row, 1 );
1084 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1086 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1087 feature->Title = msi_dup_record_field( row, 3 );
1088 feature->Description = msi_dup_record_field( row, 4 );
1090 if (!MSI_RecordIsNull(row,5))
1091 feature->Display = MSI_RecordGetInteger(row,5);
1093 feature->Level= MSI_RecordGetInteger(row,6);
1094 feature->Directory = msi_dup_record_field( row, 7 );
1095 feature->Attributes = MSI_RecordGetInteger(row,8);
1097 feature->Installed = INSTALLSTATE_UNKNOWN;
1098 feature->Action = INSTALLSTATE_UNKNOWN;
1099 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1101 list_add_tail( &package->features, &feature->entry );
1103 /* load feature components */
1105 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1106 if (rc != ERROR_SUCCESS)
1107 return ERROR_SUCCESS;
1109 ilfs.package = package;
1110 ilfs.feature = feature;
1112 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1113 msiobj_release(&view->hdr);
1114 return rc;
1117 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1119 MSIPACKAGE *package = param;
1120 MSIFEATURE *parent, *child;
1122 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1123 if (!child)
1124 return ERROR_FUNCTION_FAILED;
1126 if (!child->Feature_Parent)
1127 return ERROR_SUCCESS;
1129 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1130 if (!parent)
1131 return ERROR_FUNCTION_FAILED;
1133 add_feature_child( parent, child );
1134 return ERROR_SUCCESS;
1137 UINT msi_load_all_features( MSIPACKAGE *package )
1139 static const WCHAR query[] = {
1140 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1141 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1142 '`','D','i','s','p','l','a','y','`',0};
1143 MSIQUERY *view;
1144 UINT r;
1146 if (!list_empty(&package->features))
1147 return ERROR_SUCCESS;
1149 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1150 if (r != ERROR_SUCCESS)
1151 return r;
1153 r = MSI_IterateRecords( view, NULL, load_feature, package );
1154 if (r != ERROR_SUCCESS)
1156 msiobj_release( &view->hdr );
1157 return r;
1159 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1160 msiobj_release( &view->hdr );
1161 return r;
1164 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1166 if (!p)
1167 return p;
1168 p = strchrW(p, ch);
1169 if (!p)
1170 return p;
1171 *p = 0;
1172 return p+1;
1175 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1177 static const WCHAR query[] = {
1178 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1179 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1180 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1181 MSIQUERY *view = NULL;
1182 MSIRECORD *row = NULL;
1183 UINT r;
1185 TRACE("%s\n", debugstr_w(file->File));
1187 r = MSI_OpenQuery(package->db, &view, query, file->File);
1188 if (r != ERROR_SUCCESS)
1189 goto done;
1191 r = MSI_ViewExecute(view, NULL);
1192 if (r != ERROR_SUCCESS)
1193 goto done;
1195 r = MSI_ViewFetch(view, &row);
1196 if (r != ERROR_SUCCESS)
1197 goto done;
1199 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1200 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1201 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1202 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1203 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1205 done:
1206 if (view) msiobj_release(&view->hdr);
1207 if (row) msiobj_release(&row->hdr);
1208 return r;
1211 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1213 MSIRECORD *row;
1214 static const WCHAR query[] = {
1215 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1216 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1217 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1219 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1220 if (!row)
1222 WARN("query failed\n");
1223 return ERROR_FUNCTION_FAILED;
1226 file->disk_id = MSI_RecordGetInteger( row, 1 );
1227 msiobj_release( &row->hdr );
1228 return ERROR_SUCCESS;
1231 static UINT load_file(MSIRECORD *row, LPVOID param)
1233 MSIPACKAGE* package = param;
1234 LPCWSTR component;
1235 MSIFILE *file;
1237 /* fill in the data */
1239 file = msi_alloc_zero( sizeof (MSIFILE) );
1240 if (!file)
1241 return ERROR_NOT_ENOUGH_MEMORY;
1243 file->File = msi_dup_record_field( row, 1 );
1245 component = MSI_RecordGetString( row, 2 );
1246 file->Component = msi_get_loaded_component( package, component );
1248 if (!file->Component)
1250 WARN("Component not found: %s\n", debugstr_w(component));
1251 msi_free(file->File);
1252 msi_free(file);
1253 return ERROR_SUCCESS;
1256 file->FileName = msi_dup_record_field( row, 3 );
1257 msi_reduce_to_long_filename( file->FileName );
1259 file->ShortName = msi_dup_record_field( row, 3 );
1260 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1262 file->FileSize = MSI_RecordGetInteger( row, 4 );
1263 file->Version = msi_dup_record_field( row, 5 );
1264 file->Language = msi_dup_record_field( row, 6 );
1265 file->Attributes = MSI_RecordGetInteger( row, 7 );
1266 file->Sequence = MSI_RecordGetInteger( row, 8 );
1268 file->state = msifs_invalid;
1270 /* if the compressed bits are not set in the file attributes,
1271 * then read the information from the package word count property
1273 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1275 file->IsCompressed = FALSE;
1277 else if (file->Attributes &
1278 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1280 file->IsCompressed = TRUE;
1282 else if (file->Attributes & msidbFileAttributesNoncompressed)
1284 file->IsCompressed = FALSE;
1286 else
1288 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1291 load_file_hash(package, file);
1292 load_file_disk_id(package, file);
1294 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1296 list_add_tail( &package->files, &file->entry );
1298 return ERROR_SUCCESS;
1301 static UINT load_all_files(MSIPACKAGE *package)
1303 static const WCHAR query[] = {
1304 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1305 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1306 '`','S','e','q','u','e','n','c','e','`', 0};
1307 MSIQUERY *view;
1308 UINT rc;
1310 if (!list_empty(&package->files))
1311 return ERROR_SUCCESS;
1313 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1314 if (rc != ERROR_SUCCESS)
1315 return ERROR_SUCCESS;
1317 rc = MSI_IterateRecords(view, NULL, load_file, package);
1318 msiobj_release(&view->hdr);
1319 return rc;
1322 static UINT load_media( MSIRECORD *row, LPVOID param )
1324 MSIPACKAGE *package = param;
1325 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1326 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1328 /* FIXME: load external cabinets and directory sources too */
1329 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1330 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1331 return ERROR_SUCCESS;
1334 static UINT load_all_media( MSIPACKAGE *package )
1336 static const WCHAR query[] = {
1337 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1338 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1339 '`','D','i','s','k','I','d','`',0};
1340 MSIQUERY *view;
1341 UINT r;
1343 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1344 if (r != ERROR_SUCCESS)
1345 return ERROR_SUCCESS;
1347 r = MSI_IterateRecords( view, NULL, load_media, package );
1348 msiobj_release( &view->hdr );
1349 return r;
1352 static UINT load_patch(MSIRECORD *row, LPVOID param)
1354 MSIPACKAGE *package = param;
1355 MSIFILEPATCH *patch;
1356 LPWSTR file_key;
1358 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1359 if (!patch)
1360 return ERROR_NOT_ENOUGH_MEMORY;
1362 file_key = msi_dup_record_field( row, 1 );
1363 patch->File = msi_get_loaded_file( package, file_key );
1364 msi_free(file_key);
1366 if( !patch->File )
1368 ERR("Failed to find target for patch in File table\n");
1369 msi_free(patch);
1370 return ERROR_FUNCTION_FAILED;
1373 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1375 /* FIXME: The database should be properly transformed */
1376 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1378 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1379 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1380 patch->IsApplied = FALSE;
1382 /* FIXME:
1383 * Header field - for patch validation.
1384 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1387 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1389 list_add_tail( &package->filepatches, &patch->entry );
1391 return ERROR_SUCCESS;
1394 static UINT load_all_patches(MSIPACKAGE *package)
1396 static const WCHAR query[] = {
1397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1398 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1399 '`','S','e','q','u','e','n','c','e','`',0};
1400 MSIQUERY *view;
1401 UINT rc;
1403 if (!list_empty(&package->filepatches))
1404 return ERROR_SUCCESS;
1406 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1407 if (rc != ERROR_SUCCESS)
1408 return ERROR_SUCCESS;
1410 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1411 msiobj_release(&view->hdr);
1412 return rc;
1415 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1417 static const WCHAR query[] = {
1418 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1419 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1420 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1421 MSIQUERY *view;
1423 folder->persistent = FALSE;
1424 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1426 if (!MSI_ViewExecute( view, NULL ))
1428 MSIRECORD *rec;
1429 if (!MSI_ViewFetch( view, &rec ))
1431 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1432 folder->persistent = TRUE;
1433 msiobj_release( &rec->hdr );
1436 msiobj_release( &view->hdr );
1438 return ERROR_SUCCESS;
1441 static UINT load_folder( MSIRECORD *row, LPVOID param )
1443 MSIPACKAGE *package = param;
1444 static WCHAR szEmpty[] = { 0 };
1445 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1446 MSIFOLDER *folder;
1448 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1449 list_init( &folder->children );
1450 folder->Directory = msi_dup_record_field( row, 1 );
1451 folder->Parent = msi_dup_record_field( row, 2 );
1452 p = msi_dup_record_field(row, 3);
1454 TRACE("%s\n", debugstr_w(folder->Directory));
1456 /* split src and target dir */
1457 tgt_short = p;
1458 src_short = folder_split_path( p, ':' );
1460 /* split the long and short paths */
1461 tgt_long = folder_split_path( tgt_short, '|' );
1462 src_long = folder_split_path( src_short, '|' );
1464 /* check for no-op dirs */
1465 if (tgt_short && !strcmpW( szDot, tgt_short ))
1466 tgt_short = szEmpty;
1467 if (src_short && !strcmpW( szDot, src_short ))
1468 src_short = szEmpty;
1470 if (!tgt_long)
1471 tgt_long = tgt_short;
1473 if (!src_short) {
1474 src_short = tgt_short;
1475 src_long = tgt_long;
1478 if (!src_long)
1479 src_long = src_short;
1481 /* FIXME: use the target short path too */
1482 folder->TargetDefault = strdupW(tgt_long);
1483 folder->SourceShortPath = strdupW(src_short);
1484 folder->SourceLongPath = strdupW(src_long);
1485 msi_free(p);
1487 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1488 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1489 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1491 load_folder_persistence( package, folder );
1493 list_add_tail( &package->folders, &folder->entry );
1494 return ERROR_SUCCESS;
1497 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1499 FolderList *fl;
1501 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1502 fl->folder = child;
1503 list_add_tail( &parent->children, &fl->entry );
1504 return ERROR_SUCCESS;
1507 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1509 MSIPACKAGE *package = param;
1510 MSIFOLDER *parent, *child;
1512 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1513 return ERROR_FUNCTION_FAILED;
1515 if (!child->Parent) return ERROR_SUCCESS;
1517 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1518 return ERROR_FUNCTION_FAILED;
1520 return add_folder_child( parent, child );
1523 static UINT load_all_folders( MSIPACKAGE *package )
1525 static const WCHAR query[] = {
1526 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1527 '`','D','i','r','e','c','t','o','r','y','`',0};
1528 MSIQUERY *view;
1529 UINT r;
1531 if (!list_empty(&package->folders))
1532 return ERROR_SUCCESS;
1534 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1535 if (r != ERROR_SUCCESS)
1536 return r;
1538 r = MSI_IterateRecords( view, NULL, load_folder, package );
1539 if (r != ERROR_SUCCESS)
1541 msiobj_release( &view->hdr );
1542 return r;
1544 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1545 msiobj_release( &view->hdr );
1546 return r;
1549 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1551 msi_set_property( package->db, szCostingComplete, szZero );
1552 msi_set_property( package->db, szRootDrive, szCRoot );
1554 load_all_folders( package );
1555 msi_load_all_components( package );
1556 msi_load_all_features( package );
1557 load_all_files( package );
1558 load_all_patches( package );
1559 load_all_media( package );
1561 return ERROR_SUCCESS;
1564 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1566 const WCHAR *action = package->script->Actions[script][index];
1567 ui_actionstart( package, action );
1568 TRACE("executing %s\n", debugstr_w(action));
1569 return ACTION_PerformAction( package, action, script );
1572 static UINT execute_script( MSIPACKAGE *package, UINT script )
1574 UINT i, rc = ERROR_SUCCESS;
1576 TRACE("executing script %u\n", script);
1578 if (!package->script)
1580 ERR("no script!\n");
1581 return ERROR_FUNCTION_FAILED;
1583 if (script == ROLLBACK_SCRIPT)
1585 for (i = package->script->ActionCount[script]; i > 0; i--)
1587 rc = execute_script_action( package, script, i - 1 );
1588 if (rc != ERROR_SUCCESS) break;
1591 else
1593 for (i = 0; i < package->script->ActionCount[script]; i++)
1595 rc = execute_script_action( package, script, i );
1596 if (rc != ERROR_SUCCESS) break;
1599 msi_free_action_script(package, script);
1600 return rc;
1603 static UINT ACTION_FileCost(MSIPACKAGE *package)
1605 return ERROR_SUCCESS;
1608 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1610 MSICOMPONENT *comp;
1611 UINT r;
1613 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1615 if (!comp->ComponentId) continue;
1617 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1618 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1619 &comp->Installed );
1620 if (r != ERROR_SUCCESS)
1621 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1622 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1623 &comp->Installed );
1624 if (r != ERROR_SUCCESS)
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1627 &comp->Installed );
1628 if (r != ERROR_SUCCESS)
1629 comp->Installed = INSTALLSTATE_ABSENT;
1633 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1635 MSIFEATURE *feature;
1637 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1639 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1641 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1642 feature->Installed = INSTALLSTATE_ABSENT;
1643 else
1644 feature->Installed = state;
1648 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1650 return (feature->Level > 0 && feature->Level <= level);
1653 static BOOL process_state_property(MSIPACKAGE* package, int level,
1654 LPCWSTR property, INSTALLSTATE state)
1656 LPWSTR override;
1657 MSIFEATURE *feature;
1659 override = msi_dup_property( package->db, property );
1660 if (!override)
1661 return FALSE;
1663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1665 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1666 continue;
1668 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1670 if (!strcmpiW( override, szAll ))
1672 if (feature->Installed != state)
1674 feature->Action = state;
1675 feature->ActionRequest = state;
1678 else
1680 LPWSTR ptr = override;
1681 LPWSTR ptr2 = strchrW(override,',');
1683 while (ptr)
1685 int len = ptr2 - ptr;
1687 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1688 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1690 if (feature->Installed != state)
1692 feature->Action = state;
1693 feature->ActionRequest = state;
1695 break;
1697 if (ptr2)
1699 ptr=ptr2+1;
1700 ptr2 = strchrW(ptr,',');
1702 else
1703 break;
1707 msi_free(override);
1708 return TRUE;
1711 static BOOL process_overrides( MSIPACKAGE *package, int level )
1713 static const WCHAR szAddLocal[] =
1714 {'A','D','D','L','O','C','A','L',0};
1715 static const WCHAR szAddSource[] =
1716 {'A','D','D','S','O','U','R','C','E',0};
1717 static const WCHAR szAdvertise[] =
1718 {'A','D','V','E','R','T','I','S','E',0};
1719 BOOL ret = FALSE;
1721 /* all these activation/deactivation things happen in order and things
1722 * later on the list override things earlier on the list.
1724 * 0 INSTALLLEVEL processing
1725 * 1 ADDLOCAL
1726 * 2 REMOVE
1727 * 3 ADDSOURCE
1728 * 4 ADDDEFAULT
1729 * 5 REINSTALL
1730 * 6 ADVERTISE
1731 * 7 COMPADDLOCAL
1732 * 8 COMPADDSOURCE
1733 * 9 FILEADDLOCAL
1734 * 10 FILEADDSOURCE
1735 * 11 FILEADDDEFAULT
1737 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1738 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1739 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1740 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1741 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1743 if (ret)
1744 msi_set_property( package->db, szPreselected, szOne );
1746 return ret;
1749 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1751 int level;
1752 MSICOMPONENT* component;
1753 MSIFEATURE *feature;
1755 TRACE("Checking Install Level\n");
1757 level = msi_get_property_int(package->db, szInstallLevel, 1);
1759 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1761 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1763 if (!is_feature_selected( feature, level )) continue;
1765 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1767 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1769 feature->Action = INSTALLSTATE_SOURCE;
1770 feature->ActionRequest = INSTALLSTATE_SOURCE;
1772 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1774 feature->Action = INSTALLSTATE_ADVERTISED;
1775 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1777 else
1779 feature->Action = INSTALLSTATE_LOCAL;
1780 feature->ActionRequest = INSTALLSTATE_LOCAL;
1784 /* disable child features of unselected parent or follow parent */
1785 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1787 FeatureList *fl;
1789 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1791 if (!is_feature_selected( feature, level ))
1793 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1794 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1796 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1798 fl->feature->Action = feature->Action;
1799 fl->feature->ActionRequest = feature->ActionRequest;
1804 else /* preselected */
1806 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1808 if (!is_feature_selected( feature, level )) continue;
1810 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1812 if (feature->Installed == INSTALLSTATE_ABSENT)
1814 feature->Action = INSTALLSTATE_UNKNOWN;
1815 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1817 else
1819 feature->Action = feature->Installed;
1820 feature->ActionRequest = feature->Installed;
1824 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1826 FeatureList *fl;
1828 if (!is_feature_selected( feature, level )) continue;
1830 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1832 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1834 fl->feature->Action = feature->Action;
1835 fl->feature->ActionRequest = feature->ActionRequest;
1841 /* now we want to set component state based based on feature state */
1842 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 ComponentList *cl;
1846 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1847 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1848 feature->ActionRequest, feature->Action);
1850 if (!is_feature_selected( feature, level )) continue;
1852 /* features with components that have compressed files are made local */
1853 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1855 if (cl->component->ForceLocalState &&
1856 feature->ActionRequest == INSTALLSTATE_SOURCE)
1858 feature->Action = INSTALLSTATE_LOCAL;
1859 feature->ActionRequest = INSTALLSTATE_LOCAL;
1860 break;
1864 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1866 component = cl->component;
1868 switch (feature->ActionRequest)
1870 case INSTALLSTATE_ABSENT:
1871 component->anyAbsent = 1;
1872 break;
1873 case INSTALLSTATE_ADVERTISED:
1874 component->hasAdvertiseFeature = 1;
1875 break;
1876 case INSTALLSTATE_SOURCE:
1877 component->hasSourceFeature = 1;
1878 break;
1879 case INSTALLSTATE_LOCAL:
1880 component->hasLocalFeature = 1;
1881 break;
1882 case INSTALLSTATE_DEFAULT:
1883 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1884 component->hasAdvertiseFeature = 1;
1885 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1886 component->hasSourceFeature = 1;
1887 else
1888 component->hasLocalFeature = 1;
1889 break;
1890 default:
1891 break;
1896 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1898 /* check if it's local or source */
1899 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1900 (component->hasLocalFeature || component->hasSourceFeature))
1902 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1903 !component->ForceLocalState)
1905 component->Action = INSTALLSTATE_SOURCE;
1906 component->ActionRequest = INSTALLSTATE_SOURCE;
1908 else
1910 component->Action = INSTALLSTATE_LOCAL;
1911 component->ActionRequest = INSTALLSTATE_LOCAL;
1913 continue;
1916 /* if any feature is local, the component must be local too */
1917 if (component->hasLocalFeature)
1919 component->Action = INSTALLSTATE_LOCAL;
1920 component->ActionRequest = INSTALLSTATE_LOCAL;
1921 continue;
1923 if (component->hasSourceFeature)
1925 component->Action = INSTALLSTATE_SOURCE;
1926 component->ActionRequest = INSTALLSTATE_SOURCE;
1927 continue;
1929 if (component->hasAdvertiseFeature)
1931 component->Action = INSTALLSTATE_ADVERTISED;
1932 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1933 continue;
1935 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1936 if (component->anyAbsent &&
1937 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1939 component->Action = INSTALLSTATE_ABSENT;
1940 component->ActionRequest = INSTALLSTATE_ABSENT;
1944 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1946 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1948 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1949 component->Action = INSTALLSTATE_LOCAL;
1950 component->ActionRequest = INSTALLSTATE_LOCAL;
1953 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1954 component->Installed == INSTALLSTATE_SOURCE &&
1955 component->hasSourceFeature)
1957 component->Action = INSTALLSTATE_UNKNOWN;
1958 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1961 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1962 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1965 return ERROR_SUCCESS;
1968 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1970 MSIPACKAGE *package = param;
1971 LPCWSTR name;
1972 MSIFEATURE *feature;
1974 name = MSI_RecordGetString( row, 1 );
1976 feature = msi_get_loaded_feature( package, name );
1977 if (!feature)
1978 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1979 else
1981 LPCWSTR Condition;
1982 Condition = MSI_RecordGetString(row,3);
1984 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1986 int level = MSI_RecordGetInteger(row,2);
1987 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1988 feature->Level = level;
1991 return ERROR_SUCCESS;
1994 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1996 static const WCHAR name[] = {'\\',0};
1997 VS_FIXEDFILEINFO *ptr, *ret;
1998 LPVOID version;
1999 DWORD versize, handle;
2000 UINT sz;
2002 versize = GetFileVersionInfoSizeW( filename, &handle );
2003 if (!versize)
2004 return NULL;
2006 version = msi_alloc( versize );
2007 if (!version)
2008 return NULL;
2010 GetFileVersionInfoW( filename, 0, versize, version );
2012 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2014 msi_free( version );
2015 return NULL;
2018 ret = msi_alloc( sz );
2019 memcpy( ret, ptr, sz );
2021 msi_free( version );
2022 return ret;
2025 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2027 DWORD ms, ls;
2029 msi_parse_version_string( version, &ms, &ls );
2031 if (fi->dwFileVersionMS > ms) return 1;
2032 else if (fi->dwFileVersionMS < ms) return -1;
2033 else if (fi->dwFileVersionLS > ls) return 1;
2034 else if (fi->dwFileVersionLS < ls) return -1;
2035 return 0;
2038 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2040 DWORD ms1, ms2;
2042 msi_parse_version_string( ver1, &ms1, NULL );
2043 msi_parse_version_string( ver2, &ms2, NULL );
2045 if (ms1 > ms2) return 1;
2046 else if (ms1 < ms2) return -1;
2047 return 0;
2050 DWORD msi_get_disk_file_size( LPCWSTR filename )
2052 HANDLE file;
2053 DWORD size;
2055 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2056 if (file == INVALID_HANDLE_VALUE)
2057 return INVALID_FILE_SIZE;
2059 size = GetFileSize( file, NULL );
2060 TRACE("size is %u\n", size);
2061 CloseHandle( file );
2062 return size;
2065 BOOL msi_file_hash_matches( MSIFILE *file )
2067 UINT r;
2068 MSIFILEHASHINFO hash;
2070 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2071 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2072 if (r != ERROR_SUCCESS)
2073 return FALSE;
2075 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2078 static WCHAR *get_temp_dir( void )
2080 static UINT id;
2081 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2083 GetTempPathW( MAX_PATH, tmp );
2084 for (;;)
2086 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2087 if (CreateDirectoryW( dir, NULL )) break;
2089 return strdupW( dir );
2093 * msi_build_directory_name()
2095 * This function is to save messing round with directory names
2096 * It handles adding backslashes between path segments,
2097 * and can add \ at the end of the directory name if told to.
2099 * It takes a variable number of arguments.
2100 * It always allocates a new string for the result, so make sure
2101 * to free the return value when finished with it.
2103 * The first arg is the number of path segments that follow.
2104 * The arguments following count are a list of path segments.
2105 * A path segment may be NULL.
2107 * Path segments will be added with a \ separating them.
2108 * A \ will not be added after the last segment, however if the
2109 * last segment is NULL, then the last character will be a \
2111 WCHAR *msi_build_directory_name( DWORD count, ... )
2113 DWORD sz = 1, i;
2114 WCHAR *dir;
2115 va_list va;
2117 va_start( va, count );
2118 for (i = 0; i < count; i++)
2120 const WCHAR *str = va_arg( va, const WCHAR * );
2121 if (str) sz += strlenW( str ) + 1;
2123 va_end( va );
2125 dir = msi_alloc( sz * sizeof(WCHAR) );
2126 dir[0] = 0;
2128 va_start( va, count );
2129 for (i = 0; i < count; i++)
2131 const WCHAR *str = va_arg( va, const WCHAR * );
2132 if (!str) continue;
2133 strcatW( dir, str );
2134 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2136 va_end( va );
2137 return dir;
2140 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2142 MSIASSEMBLY *assembly = file->Component->assembly;
2144 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2146 msi_free( file->TargetPath );
2147 if (assembly && !assembly->application)
2149 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2150 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2151 msi_track_tempfile( package, file->TargetPath );
2153 else
2155 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2156 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2159 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2162 static UINT calculate_file_cost( MSIPACKAGE *package )
2164 VS_FIXEDFILEINFO *file_version;
2165 WCHAR *font_version;
2166 MSIFILE *file;
2168 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2170 MSICOMPONENT *comp = file->Component;
2171 DWORD file_size;
2173 if (!comp->Enabled) continue;
2175 if (file->IsCompressed)
2176 comp->ForceLocalState = TRUE;
2178 set_target_path( package, file );
2180 if ((comp->assembly && !comp->assembly->installed) ||
2181 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2183 comp->Cost += file->FileSize;
2184 continue;
2186 file_size = msi_get_disk_file_size( file->TargetPath );
2188 if (file->Version)
2190 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2192 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2194 comp->Cost += file->FileSize - file_size;
2196 msi_free( file_version );
2197 continue;
2199 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2201 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2203 comp->Cost += file->FileSize - file_size;
2205 msi_free( font_version );
2206 continue;
2209 if (file_size != file->FileSize)
2211 comp->Cost += file->FileSize - file_size;
2214 return ERROR_SUCCESS;
2217 void msi_clean_path( WCHAR *p )
2219 WCHAR *q = p;
2220 int n, len = 0;
2222 while (1)
2224 /* copy until the end of the string or a space */
2225 while (*p != ' ' && (*q = *p))
2227 p++, len++;
2228 /* reduce many backslashes to one */
2229 if (*p != '\\' || *q != '\\')
2230 q++;
2233 /* quit at the end of the string */
2234 if (!*p)
2235 break;
2237 /* count the number of spaces */
2238 n = 0;
2239 while (p[n] == ' ')
2240 n++;
2242 /* if it's leading or trailing space, skip it */
2243 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2244 p += n;
2245 else /* copy n spaces */
2246 while (n && (*q++ = *p++)) n--;
2250 static WCHAR *get_target_dir_property( MSIDATABASE *db )
2252 int len;
2253 WCHAR *path, *target_dir = msi_dup_property( db, szTargetDir );
2255 if (!target_dir) return NULL;
2257 len = strlenW( target_dir );
2258 if (target_dir[len - 1] == '\\') return target_dir;
2259 if ((path = msi_alloc( (len + 2) * sizeof(WCHAR) )))
2261 strcpyW( path, target_dir );
2262 path[len] = '\\';
2263 path[len + 1] = 0;
2265 msi_free( target_dir );
2266 return path;
2269 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2271 FolderList *fl;
2272 MSIFOLDER *folder, *parent, *child;
2273 WCHAR *path;
2275 TRACE("resolving %s\n", debugstr_w(name));
2277 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2279 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2281 if (!load_prop || !(path = get_target_dir_property( package->db )))
2283 path = msi_dup_property( package->db, szRootDrive );
2286 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2288 parent = msi_get_loaded_folder( package, folder->Parent );
2289 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2291 msi_clean_path( path );
2292 if (folder->ResolvedTarget && !strcmpiW( path, folder->ResolvedTarget ))
2294 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2295 msi_free( path );
2296 return;
2298 msi_set_property( package->db, folder->Directory, path );
2299 msi_free( folder->ResolvedTarget );
2300 folder->ResolvedTarget = path;
2302 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2304 child = fl->folder;
2305 msi_resolve_target_folder( package, child->Directory, load_prop );
2307 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2310 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2312 static const WCHAR query[] = {
2313 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2314 '`','C','o','n','d','i','t','i','o','n','`',0};
2315 static const WCHAR szOutOfDiskSpace[] = {
2316 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2317 MSICOMPONENT *comp;
2318 MSIQUERY *view;
2319 LPWSTR level;
2320 UINT rc;
2322 TRACE("Building directory properties\n");
2323 msi_resolve_target_folder( package, szTargetDir, TRUE );
2325 TRACE("Evaluating component conditions\n");
2326 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2328 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2330 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2331 comp->Enabled = FALSE;
2333 else
2334 comp->Enabled = TRUE;
2337 /* read components states from the registry */
2338 ACTION_GetComponentInstallStates(package);
2339 ACTION_GetFeatureInstallStates(package);
2341 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2343 TRACE("Evaluating feature conditions\n");
2345 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2346 if (rc == ERROR_SUCCESS)
2348 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2349 msiobj_release( &view->hdr );
2350 if (rc != ERROR_SUCCESS)
2351 return rc;
2355 TRACE("Calculating file cost\n");
2356 calculate_file_cost( package );
2358 msi_set_property( package->db, szCostingComplete, szOne );
2359 /* set default run level if not set */
2360 level = msi_dup_property( package->db, szInstallLevel );
2361 if (!level)
2362 msi_set_property( package->db, szInstallLevel, szOne );
2363 msi_free(level);
2365 /* FIXME: check volume disk space */
2366 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2368 return MSI_SetFeatureStates(package);
2371 /* OK this value is "interpreted" and then formatted based on the
2372 first few characters */
2373 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2374 DWORD *size)
2376 LPSTR data = NULL;
2378 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2380 if (value[1]=='x')
2382 LPWSTR ptr;
2383 CHAR byte[5];
2384 LPWSTR deformated = NULL;
2385 int count;
2387 deformat_string(package, &value[2], &deformated);
2389 /* binary value type */
2390 ptr = deformated;
2391 *type = REG_BINARY;
2392 if (strlenW(ptr)%2)
2393 *size = (strlenW(ptr)/2)+1;
2394 else
2395 *size = strlenW(ptr)/2;
2397 data = msi_alloc(*size);
2399 byte[0] = '0';
2400 byte[1] = 'x';
2401 byte[4] = 0;
2402 count = 0;
2403 /* if uneven pad with a zero in front */
2404 if (strlenW(ptr)%2)
2406 byte[2]= '0';
2407 byte[3]= *ptr;
2408 ptr++;
2409 data[count] = (BYTE)strtol(byte,NULL,0);
2410 count ++;
2411 TRACE("Uneven byte count\n");
2413 while (*ptr)
2415 byte[2]= *ptr;
2416 ptr++;
2417 byte[3]= *ptr;
2418 ptr++;
2419 data[count] = (BYTE)strtol(byte,NULL,0);
2420 count ++;
2422 msi_free(deformated);
2424 TRACE("Data %i bytes(%i)\n",*size,count);
2426 else
2428 LPWSTR deformated;
2429 LPWSTR p;
2430 DWORD d = 0;
2431 deformat_string(package, &value[1], &deformated);
2433 *type=REG_DWORD;
2434 *size = sizeof(DWORD);
2435 data = msi_alloc(*size);
2436 p = deformated;
2437 if (*p == '-')
2438 p++;
2439 while (*p)
2441 if ( (*p < '0') || (*p > '9') )
2442 break;
2443 d *= 10;
2444 d += (*p - '0');
2445 p++;
2447 if (deformated[0] == '-')
2448 d = -d;
2449 *(LPDWORD)data = d;
2450 TRACE("DWORD %i\n",*(LPDWORD)data);
2452 msi_free(deformated);
2455 else
2457 static const WCHAR szMulti[] = {'[','~',']',0};
2458 LPCWSTR ptr;
2459 *type=REG_SZ;
2461 if (value[0]=='#')
2463 if (value[1]=='%')
2465 ptr = &value[2];
2466 *type=REG_EXPAND_SZ;
2468 else
2469 ptr = &value[1];
2471 else
2472 ptr=value;
2474 if (strstrW(value, szMulti))
2475 *type = REG_MULTI_SZ;
2477 /* remove initial delimiter */
2478 if (!strncmpW(value, szMulti, 3))
2479 ptr = value + 3;
2481 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2483 /* add double NULL terminator */
2484 if (*type == REG_MULTI_SZ)
2486 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2487 data = msi_realloc_zero(data, *size);
2490 return data;
2493 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2495 const WCHAR *ret;
2497 switch (root)
2499 case -1:
2500 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2502 *root_key = HKEY_LOCAL_MACHINE;
2503 ret = szHLM;
2505 else
2507 *root_key = HKEY_CURRENT_USER;
2508 ret = szHCU;
2510 break;
2511 case 0:
2512 *root_key = HKEY_CLASSES_ROOT;
2513 ret = szHCR;
2514 break;
2515 case 1:
2516 *root_key = HKEY_CURRENT_USER;
2517 ret = szHCU;
2518 break;
2519 case 2:
2520 *root_key = HKEY_LOCAL_MACHINE;
2521 ret = szHLM;
2522 break;
2523 case 3:
2524 *root_key = HKEY_USERS;
2525 ret = szHU;
2526 break;
2527 default:
2528 ERR("Unknown root %i\n", root);
2529 return NULL;
2532 return ret;
2535 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2537 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2538 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2540 if (is_64bit && package->platform == PLATFORM_INTEL &&
2541 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2543 UINT size;
2544 WCHAR *path_32node;
2546 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2547 if (!(path_32node = msi_alloc( size ))) return NULL;
2549 memcpy( path_32node, path, len * sizeof(WCHAR) );
2550 strcpyW( path_32node + len, szWow6432Node );
2551 strcatW( path_32node, szBackSlash );
2552 strcatW( path_32node, path + len );
2553 return path_32node;
2556 return strdupW( path );
2559 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2561 MSIPACKAGE *package = param;
2562 LPSTR value_data = NULL;
2563 HKEY root_key, hkey;
2564 DWORD type,size;
2565 LPWSTR deformated, uikey, keypath;
2566 LPCWSTR szRoot, component, name, key, value;
2567 MSICOMPONENT *comp;
2568 MSIRECORD * uirow;
2569 INT root;
2570 BOOL check_first = FALSE;
2571 UINT rc;
2573 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2575 component = MSI_RecordGetString(row, 6);
2576 comp = msi_get_loaded_component(package,component);
2577 if (!comp)
2578 return ERROR_SUCCESS;
2580 comp->Action = msi_get_component_action( package, comp );
2581 if (comp->Action != INSTALLSTATE_LOCAL)
2583 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2584 return ERROR_SUCCESS;
2587 name = MSI_RecordGetString(row, 4);
2588 if( MSI_RecordIsNull(row,5) && name )
2590 /* null values can have special meanings */
2591 if (name[0]=='-' && name[1] == 0)
2592 return ERROR_SUCCESS;
2593 else if ((name[0]=='+' && name[1] == 0) ||
2594 (name[0] == '*' && name[1] == 0))
2595 name = NULL;
2596 check_first = TRUE;
2599 root = MSI_RecordGetInteger(row,2);
2600 key = MSI_RecordGetString(row, 3);
2602 szRoot = get_root_key( package, root, &root_key );
2603 if (!szRoot)
2604 return ERROR_SUCCESS;
2606 deformat_string(package, key , &deformated);
2607 size = strlenW(deformated) + strlenW(szRoot) + 1;
2608 uikey = msi_alloc(size*sizeof(WCHAR));
2609 strcpyW(uikey,szRoot);
2610 strcatW(uikey,deformated);
2612 keypath = get_keypath( package, root_key, deformated );
2613 msi_free( deformated );
2614 if (RegCreateKeyW( root_key, keypath, &hkey ))
2616 ERR("Could not create key %s\n", debugstr_w(keypath));
2617 msi_free(uikey);
2618 msi_free(keypath);
2619 return ERROR_SUCCESS;
2622 value = MSI_RecordGetString(row,5);
2623 if (value)
2624 value_data = parse_value(package, value, &type, &size);
2625 else
2627 value_data = (LPSTR)strdupW(szEmpty);
2628 size = sizeof(szEmpty);
2629 type = REG_SZ;
2632 deformat_string(package, name, &deformated);
2634 if (!check_first)
2636 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2637 debugstr_w(uikey));
2638 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2640 else
2642 DWORD sz = 0;
2643 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2644 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2646 TRACE("value %s of %s checked already exists\n",
2647 debugstr_w(deformated), debugstr_w(uikey));
2649 else
2651 TRACE("Checked and setting value %s of %s\n",
2652 debugstr_w(deformated), debugstr_w(uikey));
2653 if (deformated || size)
2654 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2657 RegCloseKey(hkey);
2659 uirow = MSI_CreateRecord(3);
2660 MSI_RecordSetStringW(uirow,2,deformated);
2661 MSI_RecordSetStringW(uirow,1,uikey);
2662 if (type == REG_SZ || type == REG_EXPAND_SZ)
2663 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2664 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2665 msiobj_release( &uirow->hdr );
2667 msi_free(value_data);
2668 msi_free(deformated);
2669 msi_free(uikey);
2670 msi_free(keypath);
2672 return ERROR_SUCCESS;
2675 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2677 static const WCHAR query[] = {
2678 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2679 '`','R','e','g','i','s','t','r','y','`',0};
2680 MSIQUERY *view;
2681 UINT rc;
2683 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2684 if (rc != ERROR_SUCCESS)
2685 return ERROR_SUCCESS;
2687 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2688 msiobj_release(&view->hdr);
2689 return rc;
2692 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2694 LONG res;
2695 HKEY hkey;
2696 DWORD num_subkeys, num_values;
2698 if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2700 if ((res = RegDeleteValueW( hkey, value )))
2702 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2704 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2705 NULL, NULL, NULL, NULL );
2706 RegCloseKey( hkey );
2707 if (!res && !num_subkeys && !num_values)
2709 TRACE("removing empty key %s\n", debugstr_w(keypath));
2710 RegDeleteKeyW( root, keypath );
2712 return;
2714 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2717 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2719 LONG res = RegDeleteTreeW( root, keypath );
2720 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2723 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2725 MSIPACKAGE *package = param;
2726 LPCWSTR component, name, key_str, root_key_str;
2727 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2728 MSICOMPONENT *comp;
2729 MSIRECORD *uirow;
2730 BOOL delete_key = FALSE;
2731 HKEY hkey_root;
2732 UINT size;
2733 INT root;
2735 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2737 component = MSI_RecordGetString( row, 6 );
2738 comp = msi_get_loaded_component( package, component );
2739 if (!comp)
2740 return ERROR_SUCCESS;
2742 comp->Action = msi_get_component_action( package, comp );
2743 if (comp->Action != INSTALLSTATE_ABSENT)
2745 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2746 return ERROR_SUCCESS;
2749 name = MSI_RecordGetString( row, 4 );
2750 if (MSI_RecordIsNull( row, 5 ) && name )
2752 if (name[0] == '+' && !name[1])
2753 return ERROR_SUCCESS;
2754 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2756 delete_key = TRUE;
2757 name = NULL;
2761 root = MSI_RecordGetInteger( row, 2 );
2762 key_str = MSI_RecordGetString( row, 3 );
2764 root_key_str = get_root_key( package, root, &hkey_root );
2765 if (!root_key_str)
2766 return ERROR_SUCCESS;
2768 deformat_string( package, key_str, &deformated_key );
2769 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2770 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2771 strcpyW( ui_key_str, root_key_str );
2772 strcatW( ui_key_str, deformated_key );
2774 deformat_string( package, name, &deformated_name );
2776 keypath = get_keypath( package, hkey_root, deformated_key );
2777 msi_free( deformated_key );
2778 if (delete_key) delete_reg_key( hkey_root, keypath );
2779 else delete_reg_value( hkey_root, keypath, deformated_name );
2780 msi_free( keypath );
2782 uirow = MSI_CreateRecord( 2 );
2783 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2784 MSI_RecordSetStringW( uirow, 2, deformated_name );
2785 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2786 msiobj_release( &uirow->hdr );
2788 msi_free( ui_key_str );
2789 msi_free( deformated_name );
2790 return ERROR_SUCCESS;
2793 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2795 MSIPACKAGE *package = param;
2796 LPCWSTR component, name, key_str, root_key_str;
2797 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2798 MSICOMPONENT *comp;
2799 MSIRECORD *uirow;
2800 BOOL delete_key = FALSE;
2801 HKEY hkey_root;
2802 UINT size;
2803 INT root;
2805 component = MSI_RecordGetString( row, 5 );
2806 comp = msi_get_loaded_component( package, component );
2807 if (!comp)
2808 return ERROR_SUCCESS;
2810 comp->Action = msi_get_component_action( package, comp );
2811 if (comp->Action != INSTALLSTATE_LOCAL)
2813 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2814 return ERROR_SUCCESS;
2817 if ((name = MSI_RecordGetString( row, 4 )))
2819 if (name[0] == '-' && !name[1])
2821 delete_key = TRUE;
2822 name = NULL;
2826 root = MSI_RecordGetInteger( row, 2 );
2827 key_str = MSI_RecordGetString( row, 3 );
2829 root_key_str = get_root_key( package, root, &hkey_root );
2830 if (!root_key_str)
2831 return ERROR_SUCCESS;
2833 deformat_string( package, key_str, &deformated_key );
2834 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2835 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2836 strcpyW( ui_key_str, root_key_str );
2837 strcatW( ui_key_str, deformated_key );
2839 deformat_string( package, name, &deformated_name );
2841 keypath = get_keypath( package, hkey_root, deformated_key );
2842 msi_free( deformated_key );
2843 if (delete_key) delete_reg_key( hkey_root, keypath );
2844 else delete_reg_value( hkey_root, keypath, deformated_name );
2845 msi_free( keypath );
2847 uirow = MSI_CreateRecord( 2 );
2848 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2849 MSI_RecordSetStringW( uirow, 2, deformated_name );
2850 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2851 msiobj_release( &uirow->hdr );
2853 msi_free( ui_key_str );
2854 msi_free( deformated_name );
2855 return ERROR_SUCCESS;
2858 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2860 static const WCHAR registry_query[] = {
2861 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2862 '`','R','e','g','i','s','t','r','y','`',0};
2863 static const WCHAR remove_registry_query[] = {
2864 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2865 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2866 MSIQUERY *view;
2867 UINT rc;
2869 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2870 if (rc == ERROR_SUCCESS)
2872 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2873 msiobj_release( &view->hdr );
2874 if (rc != ERROR_SUCCESS)
2875 return rc;
2877 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2878 if (rc == ERROR_SUCCESS)
2880 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2881 msiobj_release( &view->hdr );
2882 if (rc != ERROR_SUCCESS)
2883 return rc;
2885 return ERROR_SUCCESS;
2888 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2890 package->script->CurrentlyScripting = TRUE;
2892 return ERROR_SUCCESS;
2896 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2898 static const WCHAR query[]= {
2899 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2900 '`','R','e','g','i','s','t','r','y','`',0};
2901 MSICOMPONENT *comp;
2902 DWORD total = 0, count = 0;
2903 MSIQUERY *view;
2904 MSIFEATURE *feature;
2905 MSIFILE *file;
2906 UINT rc;
2908 TRACE("InstallValidate\n");
2910 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2911 if (rc == ERROR_SUCCESS)
2913 rc = MSI_IterateRecords( view, &count, NULL, package );
2914 msiobj_release( &view->hdr );
2915 if (rc != ERROR_SUCCESS)
2916 return rc;
2917 total += count * REG_PROGRESS_VALUE;
2919 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2920 total += COMPONENT_PROGRESS_VALUE;
2922 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2923 total += file->FileSize;
2925 msi_ui_progress( package, 0, total, 0, 0 );
2927 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2929 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2930 debugstr_w(feature->Feature), feature->Installed,
2931 feature->ActionRequest, feature->Action);
2933 return ERROR_SUCCESS;
2936 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2938 MSIPACKAGE* package = param;
2939 LPCWSTR cond = NULL;
2940 LPCWSTR message = NULL;
2941 UINT r;
2943 static const WCHAR title[]=
2944 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2946 cond = MSI_RecordGetString(row,1);
2948 r = MSI_EvaluateConditionW(package,cond);
2949 if (r == MSICONDITION_FALSE)
2951 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2953 LPWSTR deformated;
2954 message = MSI_RecordGetString(row,2);
2955 deformat_string(package,message,&deformated);
2956 MessageBoxW(NULL,deformated,title,MB_OK);
2957 msi_free(deformated);
2960 return ERROR_INSTALL_FAILURE;
2963 return ERROR_SUCCESS;
2966 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2968 static const WCHAR query[] = {
2969 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2970 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2971 MSIQUERY *view;
2972 UINT rc;
2974 TRACE("Checking launch conditions\n");
2976 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2977 if (rc != ERROR_SUCCESS)
2978 return ERROR_SUCCESS;
2980 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2981 msiobj_release(&view->hdr);
2982 return rc;
2985 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2988 if (!cmp->KeyPath)
2989 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2991 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2993 static const WCHAR query[] = {
2994 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2995 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2996 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2997 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
2998 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2999 MSIRECORD *row;
3000 UINT root, len;
3001 LPWSTR deformated, buffer, deformated_name;
3002 LPCWSTR key, name;
3004 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3005 if (!row)
3006 return NULL;
3008 root = MSI_RecordGetInteger(row,2);
3009 key = MSI_RecordGetString(row, 3);
3010 name = MSI_RecordGetString(row, 4);
3011 deformat_string(package, key , &deformated);
3012 deformat_string(package, name, &deformated_name);
3014 len = strlenW(deformated) + 6;
3015 if (deformated_name)
3016 len+=strlenW(deformated_name);
3018 buffer = msi_alloc( len *sizeof(WCHAR));
3020 if (deformated_name)
3021 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3022 else
3023 sprintfW(buffer,fmt,root,deformated);
3025 msi_free(deformated);
3026 msi_free(deformated_name);
3027 msiobj_release(&row->hdr);
3029 return buffer;
3031 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3033 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3034 return NULL;
3036 else
3038 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3040 if (file)
3041 return strdupW( file->TargetPath );
3043 return NULL;
3046 static HKEY openSharedDLLsKey(void)
3048 HKEY hkey=0;
3049 static const WCHAR path[] =
3050 {'S','o','f','t','w','a','r','e','\\',
3051 'M','i','c','r','o','s','o','f','t','\\',
3052 'W','i','n','d','o','w','s','\\',
3053 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3054 'S','h','a','r','e','d','D','L','L','s',0};
3056 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3057 return hkey;
3060 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3062 HKEY hkey;
3063 DWORD count=0;
3064 DWORD type;
3065 DWORD sz = sizeof(count);
3066 DWORD rc;
3068 hkey = openSharedDLLsKey();
3069 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3070 if (rc != ERROR_SUCCESS)
3071 count = 0;
3072 RegCloseKey(hkey);
3073 return count;
3076 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3078 HKEY hkey;
3080 hkey = openSharedDLLsKey();
3081 if (count > 0)
3082 msi_reg_set_val_dword( hkey, path, count );
3083 else
3084 RegDeleteValueW(hkey,path);
3085 RegCloseKey(hkey);
3086 return count;
3089 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3091 MSIFEATURE *feature;
3092 INT count = 0;
3093 BOOL write = FALSE;
3095 /* only refcount DLLs */
3096 if (comp->KeyPath == NULL ||
3097 comp->assembly ||
3098 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3099 comp->Attributes & msidbComponentAttributesODBCDataSource)
3100 write = FALSE;
3101 else
3103 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3104 write = (count > 0);
3106 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3107 write = TRUE;
3110 /* increment counts */
3111 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3113 ComponentList *cl;
3115 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3116 continue;
3118 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3120 if ( cl->component == comp )
3121 count++;
3125 /* decrement counts */
3126 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3128 ComponentList *cl;
3130 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3131 continue;
3133 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3135 if ( cl->component == comp )
3136 count--;
3140 /* ref count all the files in the component */
3141 if (write)
3143 MSIFILE *file;
3145 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3147 if (file->Component == comp)
3148 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3152 /* add a count for permanent */
3153 if (comp->Attributes & msidbComponentAttributesPermanent)
3154 count ++;
3156 comp->RefCount = count;
3158 if (write)
3159 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3162 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3164 if (comp->assembly)
3166 const WCHAR prefixW[] = {'<','\\',0};
3167 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3168 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3170 if (keypath)
3172 strcpyW( keypath, prefixW );
3173 strcatW( keypath, comp->assembly->display_name );
3175 return keypath;
3177 return resolve_keypath( package, comp );
3180 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3182 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3183 UINT rc;
3184 MSICOMPONENT *comp;
3185 HKEY hkey;
3187 TRACE("\n");
3189 squash_guid(package->ProductCode,squished_pc);
3190 msi_set_sourcedir_props(package, FALSE);
3192 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3194 MSIRECORD *uirow;
3195 INSTALLSTATE action;
3197 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3198 if (!comp->ComponentId)
3199 continue;
3201 squash_guid( comp->ComponentId, squished_cc );
3202 msi_free( comp->FullKeypath );
3203 comp->FullKeypath = build_full_keypath( package, comp );
3205 ACTION_RefCountComponent( package, comp );
3207 if (package->need_rollback) action = comp->Installed;
3208 else action = comp->ActionRequest;
3210 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3211 debugstr_w(comp->Component), debugstr_w(squished_cc),
3212 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3214 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3216 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3217 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3218 else
3219 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3221 if (rc != ERROR_SUCCESS)
3222 continue;
3224 if (comp->Attributes & msidbComponentAttributesPermanent)
3226 static const WCHAR szPermKey[] =
3227 { '0','0','0','0','0','0','0','0','0','0','0','0',
3228 '0','0','0','0','0','0','0','0','0','0','0','0',
3229 '0','0','0','0','0','0','0','0',0 };
3231 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3233 if (action == INSTALLSTATE_LOCAL)
3234 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3235 else
3237 MSIFILE *file;
3238 MSIRECORD *row;
3239 LPWSTR ptr, ptr2;
3240 WCHAR source[MAX_PATH];
3241 WCHAR base[MAX_PATH];
3242 LPWSTR sourcepath;
3244 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3245 static const WCHAR query[] = {
3246 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3247 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3248 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3249 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3250 '`','D','i','s','k','I','d','`',0};
3252 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3253 continue;
3255 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3256 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3257 ptr2 = strrchrW(source, '\\') + 1;
3258 msiobj_release(&row->hdr);
3260 lstrcpyW(base, package->PackagePath);
3261 ptr = strrchrW(base, '\\');
3262 *(ptr + 1) = '\0';
3264 sourcepath = msi_resolve_file_source(package, file);
3265 ptr = sourcepath + lstrlenW(base);
3266 lstrcpyW(ptr2, ptr);
3267 msi_free(sourcepath);
3269 msi_reg_set_val_str(hkey, squished_pc, source);
3271 RegCloseKey(hkey);
3273 else if (action == INSTALLSTATE_ABSENT)
3275 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3276 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3277 else
3278 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3281 /* UI stuff */
3282 uirow = MSI_CreateRecord(3);
3283 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3284 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3285 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3286 msi_ui_actiondata( package, szProcessComponents, uirow );
3287 msiobj_release( &uirow->hdr );
3289 return ERROR_SUCCESS;
3292 typedef struct {
3293 CLSID clsid;
3294 LPWSTR source;
3296 LPWSTR path;
3297 ITypeLib *ptLib;
3298 } typelib_struct;
3300 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3301 LPWSTR lpszName, LONG_PTR lParam)
3303 TLIBATTR *attr;
3304 typelib_struct *tl_struct = (typelib_struct*) lParam;
3305 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3306 int sz;
3307 HRESULT res;
3309 if (!IS_INTRESOURCE(lpszName))
3311 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3312 return TRUE;
3315 sz = strlenW(tl_struct->source)+4;
3316 sz *= sizeof(WCHAR);
3318 if ((INT_PTR)lpszName == 1)
3319 tl_struct->path = strdupW(tl_struct->source);
3320 else
3322 tl_struct->path = msi_alloc(sz);
3323 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3326 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3327 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3328 if (FAILED(res))
3330 msi_free(tl_struct->path);
3331 tl_struct->path = NULL;
3333 return TRUE;
3336 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3337 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3339 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3340 return FALSE;
3343 msi_free(tl_struct->path);
3344 tl_struct->path = NULL;
3346 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3347 ITypeLib_Release(tl_struct->ptLib);
3349 return TRUE;
3352 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3354 MSIPACKAGE* package = param;
3355 LPCWSTR component;
3356 MSICOMPONENT *comp;
3357 MSIFILE *file;
3358 typelib_struct tl_struct;
3359 ITypeLib *tlib;
3360 HMODULE module;
3361 HRESULT hr;
3363 component = MSI_RecordGetString(row,3);
3364 comp = msi_get_loaded_component(package,component);
3365 if (!comp)
3366 return ERROR_SUCCESS;
3368 comp->Action = msi_get_component_action( package, comp );
3369 if (comp->Action != INSTALLSTATE_LOCAL)
3371 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3372 return ERROR_SUCCESS;
3375 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3377 TRACE("component has no key path\n");
3378 return ERROR_SUCCESS;
3380 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3382 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3383 if (module)
3385 LPCWSTR guid;
3386 guid = MSI_RecordGetString(row,1);
3387 CLSIDFromString( guid, &tl_struct.clsid);
3388 tl_struct.source = strdupW( file->TargetPath );
3389 tl_struct.path = NULL;
3391 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3392 (LONG_PTR)&tl_struct);
3394 if (tl_struct.path)
3396 LPCWSTR helpid, help_path = NULL;
3397 HRESULT res;
3399 helpid = MSI_RecordGetString(row,6);
3401 if (helpid) help_path = msi_get_target_folder( package, helpid );
3402 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3404 if (FAILED(res))
3405 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3406 else
3407 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3409 ITypeLib_Release(tl_struct.ptLib);
3410 msi_free(tl_struct.path);
3412 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3414 FreeLibrary(module);
3415 msi_free(tl_struct.source);
3417 else
3419 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3420 if (FAILED(hr))
3422 ERR("Failed to load type library: %08x\n", hr);
3423 return ERROR_INSTALL_FAILURE;
3426 ITypeLib_Release(tlib);
3429 return ERROR_SUCCESS;
3432 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3434 static const WCHAR query[] = {
3435 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3436 '`','T','y','p','e','L','i','b','`',0};
3437 MSIQUERY *view;
3438 UINT rc;
3440 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3441 if (rc != ERROR_SUCCESS)
3442 return ERROR_SUCCESS;
3444 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3445 msiobj_release(&view->hdr);
3446 return rc;
3449 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3451 MSIPACKAGE *package = param;
3452 LPCWSTR component, guid;
3453 MSICOMPONENT *comp;
3454 GUID libid;
3455 UINT version;
3456 LCID language;
3457 SYSKIND syskind;
3458 HRESULT hr;
3460 component = MSI_RecordGetString( row, 3 );
3461 comp = msi_get_loaded_component( package, component );
3462 if (!comp)
3463 return ERROR_SUCCESS;
3465 comp->Action = msi_get_component_action( package, comp );
3466 if (comp->Action != INSTALLSTATE_ABSENT)
3468 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3469 return ERROR_SUCCESS;
3471 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3473 guid = MSI_RecordGetString( row, 1 );
3474 CLSIDFromString( guid, &libid );
3475 version = MSI_RecordGetInteger( row, 4 );
3476 language = MSI_RecordGetInteger( row, 2 );
3478 #ifdef _WIN64
3479 syskind = SYS_WIN64;
3480 #else
3481 syskind = SYS_WIN32;
3482 #endif
3484 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3485 if (FAILED(hr))
3487 WARN("Failed to unregister typelib: %08x\n", hr);
3490 return ERROR_SUCCESS;
3493 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3495 static const WCHAR query[] = {
3496 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3497 '`','T','y','p','e','L','i','b','`',0};
3498 MSIQUERY *view;
3499 UINT rc;
3501 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3502 if (rc != ERROR_SUCCESS)
3503 return ERROR_SUCCESS;
3505 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3506 msiobj_release( &view->hdr );
3507 return rc;
3510 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3512 static const WCHAR szlnk[] = {'.','l','n','k',0};
3513 LPCWSTR directory, extension, link_folder;
3514 LPWSTR link_file, filename;
3516 directory = MSI_RecordGetString( row, 2 );
3517 link_folder = msi_get_target_folder( package, directory );
3518 if (!link_folder)
3520 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3521 return NULL;
3523 /* may be needed because of a bug somewhere else */
3524 msi_create_full_path( link_folder );
3526 filename = msi_dup_record_field( row, 3 );
3527 msi_reduce_to_long_filename( filename );
3529 extension = strchrW( filename, '.' );
3530 if (!extension || strcmpiW( extension, szlnk ))
3532 int len = strlenW( filename );
3533 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3534 memcpy( filename + len, szlnk, sizeof(szlnk) );
3536 link_file = msi_build_directory_name( 2, link_folder, filename );
3537 msi_free( filename );
3539 return link_file;
3542 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3544 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3545 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3546 WCHAR *folder, *dest, *path;
3548 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3549 folder = msi_dup_property( package->db, szWindowsFolder );
3550 else
3552 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3553 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3554 msi_free( appdata );
3556 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3557 msi_create_full_path( dest );
3558 path = msi_build_directory_name( 2, dest, icon_name );
3559 msi_free( folder );
3560 msi_free( dest );
3561 return path;
3564 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3566 MSIPACKAGE *package = param;
3567 LPWSTR link_file, deformated, path;
3568 LPCWSTR component, target;
3569 MSICOMPONENT *comp;
3570 IShellLinkW *sl = NULL;
3571 IPersistFile *pf = NULL;
3572 HRESULT res;
3574 component = MSI_RecordGetString(row, 4);
3575 comp = msi_get_loaded_component(package, component);
3576 if (!comp)
3577 return ERROR_SUCCESS;
3579 comp->Action = msi_get_component_action( package, comp );
3580 if (comp->Action != INSTALLSTATE_LOCAL)
3582 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3583 return ERROR_SUCCESS;
3585 msi_ui_actiondata( package, szCreateShortcuts, row );
3587 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3588 &IID_IShellLinkW, (LPVOID *) &sl );
3590 if (FAILED( res ))
3592 ERR("CLSID_ShellLink not available\n");
3593 goto err;
3596 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3597 if (FAILED( res ))
3599 ERR("QueryInterface(IID_IPersistFile) failed\n");
3600 goto err;
3603 target = MSI_RecordGetString(row, 5);
3604 if (strchrW(target, '['))
3606 int len;
3607 WCHAR *format_string, *p;
3609 if (!(p = strchrW( target, ']' ))) goto err;
3610 len = p - target + 1;
3611 format_string = msi_alloc( (len + 1) * sizeof(WCHAR) );
3612 memcpy( format_string, target, len * sizeof(WCHAR) );
3613 format_string[len] = 0;
3614 deformat_string( package, format_string, &deformated );
3615 msi_free( format_string );
3617 path = msi_alloc( (strlenW( deformated ) + strlenW( p + 1 ) + 2) * sizeof(WCHAR) );
3618 strcpyW( path, deformated );
3619 PathAddBackslashW( path );
3620 strcatW( path, p + 1 );
3621 TRACE("target path is %s\n", debugstr_w(path));
3623 IShellLinkW_SetPath( sl, path );
3624 msi_free( deformated );
3625 msi_free( path );
3627 else
3629 FIXME("poorly handled shortcut format, advertised shortcut\n");
3630 IShellLinkW_SetPath(sl,comp->FullKeypath);
3633 if (!MSI_RecordIsNull(row,6))
3635 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3636 deformat_string(package, arguments, &deformated);
3637 IShellLinkW_SetArguments(sl,deformated);
3638 msi_free(deformated);
3641 if (!MSI_RecordIsNull(row,7))
3643 LPCWSTR description = MSI_RecordGetString(row, 7);
3644 IShellLinkW_SetDescription(sl, description);
3647 if (!MSI_RecordIsNull(row,8))
3648 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3650 if (!MSI_RecordIsNull(row,9))
3652 INT index;
3653 LPCWSTR icon = MSI_RecordGetString(row, 9);
3655 path = msi_build_icon_path(package, icon);
3656 index = MSI_RecordGetInteger(row,10);
3658 /* no value means 0 */
3659 if (index == MSI_NULL_INTEGER)
3660 index = 0;
3662 IShellLinkW_SetIconLocation(sl, path, index);
3663 msi_free(path);
3666 if (!MSI_RecordIsNull(row,11))
3667 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3669 if (!MSI_RecordIsNull(row,12))
3671 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3672 full_path = msi_get_target_folder( package, wkdir );
3673 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3675 link_file = get_link_file(package, row);
3677 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3678 IPersistFile_Save(pf, link_file, FALSE);
3679 msi_free(link_file);
3681 err:
3682 if (pf)
3683 IPersistFile_Release( pf );
3684 if (sl)
3685 IShellLinkW_Release( sl );
3687 return ERROR_SUCCESS;
3690 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3692 static const WCHAR query[] = {
3693 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3694 '`','S','h','o','r','t','c','u','t','`',0};
3695 MSIQUERY *view;
3696 HRESULT res;
3697 UINT rc;
3699 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3700 if (rc != ERROR_SUCCESS)
3701 return ERROR_SUCCESS;
3703 res = CoInitialize( NULL );
3705 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3706 msiobj_release(&view->hdr);
3708 if (SUCCEEDED(res)) CoUninitialize();
3709 return rc;
3712 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3714 MSIPACKAGE *package = param;
3715 LPWSTR link_file;
3716 LPCWSTR component;
3717 MSICOMPONENT *comp;
3719 component = MSI_RecordGetString( row, 4 );
3720 comp = msi_get_loaded_component( package, component );
3721 if (!comp)
3722 return ERROR_SUCCESS;
3724 comp->Action = msi_get_component_action( package, comp );
3725 if (comp->Action != INSTALLSTATE_ABSENT)
3727 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3728 return ERROR_SUCCESS;
3730 msi_ui_actiondata( package, szRemoveShortcuts, row );
3732 link_file = get_link_file( package, row );
3734 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3735 if (!DeleteFileW( link_file ))
3737 WARN("Failed to remove shortcut file %u\n", GetLastError());
3739 msi_free( link_file );
3741 return ERROR_SUCCESS;
3744 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3746 static const WCHAR query[] = {
3747 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3748 '`','S','h','o','r','t','c','u','t','`',0};
3749 MSIQUERY *view;
3750 UINT rc;
3752 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3753 if (rc != ERROR_SUCCESS)
3754 return ERROR_SUCCESS;
3756 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3757 msiobj_release( &view->hdr );
3758 return rc;
3761 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3763 MSIPACKAGE* package = param;
3764 HANDLE the_file;
3765 LPWSTR FilePath;
3766 LPCWSTR FileName;
3767 CHAR buffer[1024];
3768 DWORD sz;
3769 UINT rc;
3771 FileName = MSI_RecordGetString(row,1);
3772 if (!FileName)
3774 ERR("Unable to get FileName\n");
3775 return ERROR_SUCCESS;
3778 FilePath = msi_build_icon_path(package, FileName);
3780 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3782 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3783 FILE_ATTRIBUTE_NORMAL, NULL);
3785 if (the_file == INVALID_HANDLE_VALUE)
3787 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3788 msi_free(FilePath);
3789 return ERROR_SUCCESS;
3794 DWORD write;
3795 sz = 1024;
3796 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3797 if (rc != ERROR_SUCCESS)
3799 ERR("Failed to get stream\n");
3800 CloseHandle(the_file);
3801 DeleteFileW(FilePath);
3802 break;
3804 WriteFile(the_file,buffer,sz,&write,NULL);
3805 } while (sz == 1024);
3807 msi_free(FilePath);
3808 CloseHandle(the_file);
3810 return ERROR_SUCCESS;
3813 static UINT msi_publish_icons(MSIPACKAGE *package)
3815 static const WCHAR query[]= {
3816 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3817 '`','I','c','o','n','`',0};
3818 MSIQUERY *view;
3819 UINT r;
3821 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3822 if (r == ERROR_SUCCESS)
3824 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3825 msiobj_release(&view->hdr);
3826 if (r != ERROR_SUCCESS)
3827 return r;
3829 return ERROR_SUCCESS;
3832 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3834 UINT r;
3835 HKEY source;
3836 LPWSTR buffer;
3837 MSIMEDIADISK *disk;
3838 MSISOURCELISTINFO *info;
3840 r = RegCreateKeyW(hkey, szSourceList, &source);
3841 if (r != ERROR_SUCCESS)
3842 return r;
3844 RegCloseKey(source);
3846 buffer = strrchrW(package->PackagePath, '\\') + 1;
3847 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3848 package->Context, MSICODE_PRODUCT,
3849 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3850 if (r != ERROR_SUCCESS)
3851 return r;
3853 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3854 package->Context, MSICODE_PRODUCT,
3855 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3856 if (r != ERROR_SUCCESS)
3857 return r;
3859 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3860 package->Context, MSICODE_PRODUCT,
3861 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3862 if (r != ERROR_SUCCESS)
3863 return r;
3865 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3867 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3868 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3869 info->options, info->value);
3870 else
3871 MsiSourceListSetInfoW(package->ProductCode, NULL,
3872 info->context, info->options,
3873 info->property, info->value);
3876 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3878 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3879 disk->context, disk->options,
3880 disk->disk_id, disk->volume_label, disk->disk_prompt);
3883 return ERROR_SUCCESS;
3886 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3888 MSIHANDLE hdb, suminfo;
3889 WCHAR guids[MAX_PATH];
3890 WCHAR packcode[SQUISH_GUID_SIZE];
3891 LPWSTR buffer;
3892 LPWSTR ptr;
3893 DWORD langid;
3894 DWORD size;
3895 UINT r;
3897 static const WCHAR szARPProductIcon[] =
3898 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3899 static const WCHAR szAssignment[] =
3900 {'A','s','s','i','g','n','m','e','n','t',0};
3901 static const WCHAR szAdvertiseFlags[] =
3902 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3903 static const WCHAR szClients[] =
3904 {'C','l','i','e','n','t','s',0};
3905 static const WCHAR szColon[] = {':',0};
3907 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3908 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3909 msi_free(buffer);
3911 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3912 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3914 /* FIXME */
3915 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3917 buffer = msi_dup_property(package->db, szARPProductIcon);
3918 if (buffer)
3920 LPWSTR path = msi_build_icon_path(package, buffer);
3921 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3922 msi_free(path);
3923 msi_free(buffer);
3926 buffer = msi_dup_property(package->db, szProductVersion);
3927 if (buffer)
3929 DWORD verdword = msi_version_str_to_dword(buffer);
3930 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3931 msi_free(buffer);
3934 msi_reg_set_val_dword(hkey, szAssignment, 0);
3935 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3936 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3937 msi_reg_set_val_str(hkey, szClients, szColon);
3939 hdb = alloc_msihandle(&package->db->hdr);
3940 if (!hdb)
3941 return ERROR_NOT_ENOUGH_MEMORY;
3943 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3944 MsiCloseHandle(hdb);
3945 if (r != ERROR_SUCCESS)
3946 goto done;
3948 size = MAX_PATH;
3949 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3950 NULL, guids, &size);
3951 if (r != ERROR_SUCCESS)
3952 goto done;
3954 ptr = strchrW(guids, ';');
3955 if (ptr) *ptr = 0;
3956 squash_guid(guids, packcode);
3957 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3959 done:
3960 MsiCloseHandle(suminfo);
3961 return ERROR_SUCCESS;
3964 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3966 UINT r;
3967 HKEY hkey;
3968 LPWSTR upgrade;
3969 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3971 upgrade = msi_dup_property(package->db, szUpgradeCode);
3972 if (!upgrade)
3973 return ERROR_SUCCESS;
3975 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3976 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3977 else
3978 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3980 if (r != ERROR_SUCCESS)
3982 WARN("failed to open upgrade code key\n");
3983 msi_free(upgrade);
3984 return ERROR_SUCCESS;
3986 squash_guid(package->ProductCode, squashed_pc);
3987 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3988 RegCloseKey(hkey);
3989 msi_free(upgrade);
3990 return ERROR_SUCCESS;
3993 static BOOL msi_check_publish(MSIPACKAGE *package)
3995 MSIFEATURE *feature;
3997 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3999 feature->Action = msi_get_feature_action( package, feature );
4000 if (feature->Action == INSTALLSTATE_LOCAL)
4001 return TRUE;
4004 return FALSE;
4007 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4009 MSIFEATURE *feature;
4011 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4013 feature->Action = msi_get_feature_action( package, feature );
4014 if (feature->Action != INSTALLSTATE_ABSENT)
4015 return FALSE;
4018 return TRUE;
4021 static UINT msi_publish_patches( MSIPACKAGE *package )
4023 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4024 WCHAR patch_squashed[GUID_SIZE];
4025 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4026 LONG res;
4027 MSIPATCHINFO *patch;
4028 UINT r;
4029 WCHAR *p, *all_patches = NULL;
4030 DWORD len = 0;
4032 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4033 if (r != ERROR_SUCCESS)
4034 return ERROR_FUNCTION_FAILED;
4036 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4037 if (res != ERROR_SUCCESS)
4039 r = ERROR_FUNCTION_FAILED;
4040 goto done;
4043 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4044 if (r != ERROR_SUCCESS)
4045 goto done;
4047 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4049 squash_guid( patch->patchcode, patch_squashed );
4050 len += strlenW( patch_squashed ) + 1;
4053 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4054 if (!all_patches)
4055 goto done;
4057 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4059 HKEY patch_key;
4061 squash_guid( patch->patchcode, p );
4062 p += strlenW( p ) + 1;
4064 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4065 (const BYTE *)patch->transforms,
4066 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4067 if (res != ERROR_SUCCESS)
4068 goto done;
4070 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4071 if (r != ERROR_SUCCESS)
4072 goto done;
4074 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4075 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4076 RegCloseKey( patch_key );
4077 if (res != ERROR_SUCCESS)
4078 goto done;
4080 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4082 res = GetLastError();
4083 ERR("Unable to copy patch package %d\n", res);
4084 goto done;
4086 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4087 if (res != ERROR_SUCCESS)
4088 goto done;
4090 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4091 RegCloseKey( patch_key );
4092 if (res != ERROR_SUCCESS)
4093 goto done;
4096 all_patches[len] = 0;
4097 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4098 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4099 if (res != ERROR_SUCCESS)
4100 goto done;
4102 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4103 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4104 if (res != ERROR_SUCCESS)
4105 r = ERROR_FUNCTION_FAILED;
4107 done:
4108 RegCloseKey( product_patches_key );
4109 RegCloseKey( patches_key );
4110 RegCloseKey( product_key );
4111 msi_free( all_patches );
4112 return r;
4115 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4117 UINT rc;
4118 HKEY hukey = NULL, hudkey = NULL;
4119 MSIRECORD *uirow;
4121 if (!list_empty(&package->patches))
4123 rc = msi_publish_patches(package);
4124 if (rc != ERROR_SUCCESS)
4125 goto end;
4128 /* FIXME: also need to publish if the product is in advertise mode */
4129 if (!msi_check_publish(package))
4130 return ERROR_SUCCESS;
4132 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4133 &hukey, TRUE);
4134 if (rc != ERROR_SUCCESS)
4135 goto end;
4137 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4138 NULL, &hudkey, TRUE);
4139 if (rc != ERROR_SUCCESS)
4140 goto end;
4142 rc = msi_publish_upgrade_code(package);
4143 if (rc != ERROR_SUCCESS)
4144 goto end;
4146 rc = msi_publish_product_properties(package, hukey);
4147 if (rc != ERROR_SUCCESS)
4148 goto end;
4150 rc = msi_publish_sourcelist(package, hukey);
4151 if (rc != ERROR_SUCCESS)
4152 goto end;
4154 rc = msi_publish_icons(package);
4156 end:
4157 uirow = MSI_CreateRecord( 1 );
4158 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4159 msi_ui_actiondata( package, szPublishProduct, uirow );
4160 msiobj_release( &uirow->hdr );
4162 RegCloseKey(hukey);
4163 RegCloseKey(hudkey);
4164 return rc;
4167 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4169 WCHAR *filename, *ptr, *folder, *ret;
4170 const WCHAR *dirprop;
4172 filename = msi_dup_record_field( row, 2 );
4173 if (filename && (ptr = strchrW( filename, '|' )))
4174 ptr++;
4175 else
4176 ptr = filename;
4178 dirprop = MSI_RecordGetString( row, 3 );
4179 if (dirprop)
4181 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4182 if (!folder) folder = msi_dup_property( package->db, dirprop );
4184 else
4185 folder = msi_dup_property( package->db, szWindowsFolder );
4187 if (!folder)
4189 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4190 msi_free( filename );
4191 return NULL;
4194 ret = msi_build_directory_name( 2, folder, ptr );
4196 msi_free( filename );
4197 msi_free( folder );
4198 return ret;
4201 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4203 MSIPACKAGE *package = param;
4204 LPCWSTR component, section, key, value, identifier;
4205 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4206 MSIRECORD * uirow;
4207 INT action;
4208 MSICOMPONENT *comp;
4210 component = MSI_RecordGetString(row, 8);
4211 comp = msi_get_loaded_component(package,component);
4212 if (!comp)
4213 return ERROR_SUCCESS;
4215 comp->Action = msi_get_component_action( package, comp );
4216 if (comp->Action != INSTALLSTATE_LOCAL)
4218 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4219 return ERROR_SUCCESS;
4222 identifier = MSI_RecordGetString(row,1);
4223 section = MSI_RecordGetString(row,4);
4224 key = MSI_RecordGetString(row,5);
4225 value = MSI_RecordGetString(row,6);
4226 action = MSI_RecordGetInteger(row,7);
4228 deformat_string(package,section,&deformated_section);
4229 deformat_string(package,key,&deformated_key);
4230 deformat_string(package,value,&deformated_value);
4232 fullname = get_ini_file_name(package, row);
4234 if (action == 0)
4236 TRACE("Adding value %s to section %s in %s\n",
4237 debugstr_w(deformated_key), debugstr_w(deformated_section),
4238 debugstr_w(fullname));
4239 WritePrivateProfileStringW(deformated_section, deformated_key,
4240 deformated_value, fullname);
4242 else if (action == 1)
4244 WCHAR returned[10];
4245 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4246 returned, 10, fullname);
4247 if (returned[0] == 0)
4249 TRACE("Adding value %s to section %s in %s\n",
4250 debugstr_w(deformated_key), debugstr_w(deformated_section),
4251 debugstr_w(fullname));
4253 WritePrivateProfileStringW(deformated_section, deformated_key,
4254 deformated_value, fullname);
4257 else if (action == 3)
4258 FIXME("Append to existing section not yet implemented\n");
4260 uirow = MSI_CreateRecord(4);
4261 MSI_RecordSetStringW(uirow,1,identifier);
4262 MSI_RecordSetStringW(uirow,2,deformated_section);
4263 MSI_RecordSetStringW(uirow,3,deformated_key);
4264 MSI_RecordSetStringW(uirow,4,deformated_value);
4265 msi_ui_actiondata( package, szWriteIniValues, uirow );
4266 msiobj_release( &uirow->hdr );
4268 msi_free(fullname);
4269 msi_free(deformated_key);
4270 msi_free(deformated_value);
4271 msi_free(deformated_section);
4272 return ERROR_SUCCESS;
4275 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4277 static const WCHAR query[] = {
4278 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4279 '`','I','n','i','F','i','l','e','`',0};
4280 MSIQUERY *view;
4281 UINT rc;
4283 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4284 if (rc != ERROR_SUCCESS)
4285 return ERROR_SUCCESS;
4287 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4288 msiobj_release(&view->hdr);
4289 return rc;
4292 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4294 MSIPACKAGE *package = param;
4295 LPCWSTR component, section, key, value, identifier;
4296 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4297 MSICOMPONENT *comp;
4298 MSIRECORD *uirow;
4299 INT action;
4301 component = MSI_RecordGetString( row, 8 );
4302 comp = msi_get_loaded_component( package, component );
4303 if (!comp)
4304 return ERROR_SUCCESS;
4306 comp->Action = msi_get_component_action( package, comp );
4307 if (comp->Action != INSTALLSTATE_ABSENT)
4309 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4310 return ERROR_SUCCESS;
4313 identifier = MSI_RecordGetString( row, 1 );
4314 section = MSI_RecordGetString( row, 4 );
4315 key = MSI_RecordGetString( row, 5 );
4316 value = MSI_RecordGetString( row, 6 );
4317 action = MSI_RecordGetInteger( row, 7 );
4319 deformat_string( package, section, &deformated_section );
4320 deformat_string( package, key, &deformated_key );
4321 deformat_string( package, value, &deformated_value );
4323 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4325 filename = get_ini_file_name( package, row );
4327 TRACE("Removing key %s from section %s in %s\n",
4328 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4330 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4332 WARN("Unable to remove key %u\n", GetLastError());
4334 msi_free( filename );
4336 else
4337 FIXME("Unsupported action %d\n", action);
4340 uirow = MSI_CreateRecord( 4 );
4341 MSI_RecordSetStringW( uirow, 1, identifier );
4342 MSI_RecordSetStringW( uirow, 2, deformated_section );
4343 MSI_RecordSetStringW( uirow, 3, deformated_key );
4344 MSI_RecordSetStringW( uirow, 4, deformated_value );
4345 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4346 msiobj_release( &uirow->hdr );
4348 msi_free( deformated_key );
4349 msi_free( deformated_value );
4350 msi_free( deformated_section );
4351 return ERROR_SUCCESS;
4354 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4356 MSIPACKAGE *package = param;
4357 LPCWSTR component, section, key, value, identifier;
4358 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4359 MSICOMPONENT *comp;
4360 MSIRECORD *uirow;
4361 INT action;
4363 component = MSI_RecordGetString( row, 8 );
4364 comp = msi_get_loaded_component( package, component );
4365 if (!comp)
4366 return ERROR_SUCCESS;
4368 comp->Action = msi_get_component_action( package, comp );
4369 if (comp->Action != INSTALLSTATE_LOCAL)
4371 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4372 return ERROR_SUCCESS;
4375 identifier = MSI_RecordGetString( row, 1 );
4376 section = MSI_RecordGetString( row, 4 );
4377 key = MSI_RecordGetString( row, 5 );
4378 value = MSI_RecordGetString( row, 6 );
4379 action = MSI_RecordGetInteger( row, 7 );
4381 deformat_string( package, section, &deformated_section );
4382 deformat_string( package, key, &deformated_key );
4383 deformat_string( package, value, &deformated_value );
4385 if (action == msidbIniFileActionRemoveLine)
4387 filename = get_ini_file_name( package, row );
4389 TRACE("Removing key %s from section %s in %s\n",
4390 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4392 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4394 WARN("Unable to remove key %u\n", GetLastError());
4396 msi_free( filename );
4398 else
4399 FIXME("Unsupported action %d\n", action);
4401 uirow = MSI_CreateRecord( 4 );
4402 MSI_RecordSetStringW( uirow, 1, identifier );
4403 MSI_RecordSetStringW( uirow, 2, deformated_section );
4404 MSI_RecordSetStringW( uirow, 3, deformated_key );
4405 MSI_RecordSetStringW( uirow, 4, deformated_value );
4406 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4407 msiobj_release( &uirow->hdr );
4409 msi_free( deformated_key );
4410 msi_free( deformated_value );
4411 msi_free( deformated_section );
4412 return ERROR_SUCCESS;
4415 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4417 static const WCHAR query[] = {
4418 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4419 '`','I','n','i','F','i','l','e','`',0};
4420 static const WCHAR remove_query[] = {
4421 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4422 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4423 MSIQUERY *view;
4424 UINT rc;
4426 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4427 if (rc == ERROR_SUCCESS)
4429 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4430 msiobj_release( &view->hdr );
4431 if (rc != ERROR_SUCCESS)
4432 return rc;
4434 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4435 if (rc == ERROR_SUCCESS)
4437 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4438 msiobj_release( &view->hdr );
4439 if (rc != ERROR_SUCCESS)
4440 return rc;
4442 return ERROR_SUCCESS;
4445 static void register_dll( const WCHAR *dll, BOOL unregister )
4447 HMODULE hmod;
4449 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4450 if (hmod)
4452 HRESULT (WINAPI *func_ptr)( void );
4453 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4455 func_ptr = (void *)GetProcAddress( hmod, func );
4456 if (func_ptr)
4458 HRESULT hr = func_ptr();
4459 if (FAILED( hr ))
4460 WARN("failed to register dll 0x%08x\n", hr);
4462 else
4463 WARN("entry point %s not found\n", func);
4464 FreeLibrary( hmod );
4465 return;
4467 WARN("failed to load library %u\n", GetLastError());
4470 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4472 MSIPACKAGE *package = param;
4473 LPCWSTR filename;
4474 MSIFILE *file;
4475 MSIRECORD *uirow;
4477 filename = MSI_RecordGetString(row,1);
4478 file = msi_get_loaded_file( package, filename );
4479 if (!file)
4481 WARN("unable to find file %s\n", debugstr_w(filename));
4482 return ERROR_SUCCESS;
4484 file->Component->Action = msi_get_component_action( package, file->Component );
4485 if (file->Component->Action != INSTALLSTATE_LOCAL)
4487 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4488 return ERROR_SUCCESS;
4491 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4492 register_dll( file->TargetPath, FALSE );
4494 uirow = MSI_CreateRecord( 2 );
4495 MSI_RecordSetStringW( uirow, 1, filename );
4496 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4497 msi_ui_actiondata( package, szSelfRegModules, uirow );
4498 msiobj_release( &uirow->hdr );
4500 return ERROR_SUCCESS;
4503 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4505 static const WCHAR query[] = {
4506 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4507 '`','S','e','l','f','R','e','g','`',0};
4508 MSIQUERY *view;
4509 UINT rc;
4511 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4512 if (rc != ERROR_SUCCESS)
4513 return ERROR_SUCCESS;
4515 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4516 msiobj_release(&view->hdr);
4517 return rc;
4520 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4522 MSIPACKAGE *package = param;
4523 LPCWSTR filename;
4524 MSIFILE *file;
4525 MSIRECORD *uirow;
4527 filename = MSI_RecordGetString( row, 1 );
4528 file = msi_get_loaded_file( package, filename );
4529 if (!file)
4531 WARN("unable to find file %s\n", debugstr_w(filename));
4532 return ERROR_SUCCESS;
4534 file->Component->Action = msi_get_component_action( package, file->Component );
4535 if (file->Component->Action != INSTALLSTATE_ABSENT)
4537 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4538 return ERROR_SUCCESS;
4541 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4542 register_dll( file->TargetPath, TRUE );
4544 uirow = MSI_CreateRecord( 2 );
4545 MSI_RecordSetStringW( uirow, 1, filename );
4546 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4547 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4548 msiobj_release( &uirow->hdr );
4550 return ERROR_SUCCESS;
4553 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4555 static const WCHAR query[] = {
4556 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4557 '`','S','e','l','f','R','e','g','`',0};
4558 MSIQUERY *view;
4559 UINT rc;
4561 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4562 if (rc != ERROR_SUCCESS)
4563 return ERROR_SUCCESS;
4565 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4566 msiobj_release( &view->hdr );
4567 return rc;
4570 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4572 MSIFEATURE *feature;
4573 UINT rc;
4574 HKEY hkey = NULL, userdata = NULL;
4576 if (!msi_check_publish(package))
4577 return ERROR_SUCCESS;
4579 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4580 &hkey, TRUE);
4581 if (rc != ERROR_SUCCESS)
4582 goto end;
4584 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4585 &userdata, TRUE);
4586 if (rc != ERROR_SUCCESS)
4587 goto end;
4589 /* here the guids are base 85 encoded */
4590 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4592 ComponentList *cl;
4593 LPWSTR data = NULL;
4594 GUID clsid;
4595 INT size;
4596 BOOL absent = FALSE;
4597 MSIRECORD *uirow;
4599 if (feature->Action != INSTALLSTATE_LOCAL &&
4600 feature->Action != INSTALLSTATE_SOURCE &&
4601 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4603 size = 1;
4604 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4606 size += 21;
4608 if (feature->Feature_Parent)
4609 size += strlenW( feature->Feature_Parent )+2;
4611 data = msi_alloc(size * sizeof(WCHAR));
4613 data[0] = 0;
4614 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4616 MSICOMPONENT* component = cl->component;
4617 WCHAR buf[21];
4619 buf[0] = 0;
4620 if (component->ComponentId)
4622 TRACE("From %s\n",debugstr_w(component->ComponentId));
4623 CLSIDFromString(component->ComponentId, &clsid);
4624 encode_base85_guid(&clsid,buf);
4625 TRACE("to %s\n",debugstr_w(buf));
4626 strcatW(data,buf);
4630 if (feature->Feature_Parent)
4632 static const WCHAR sep[] = {'\2',0};
4633 strcatW(data,sep);
4634 strcatW(data,feature->Feature_Parent);
4637 msi_reg_set_val_str( userdata, feature->Feature, data );
4638 msi_free(data);
4640 size = 0;
4641 if (feature->Feature_Parent)
4642 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4643 if (!absent)
4645 size += sizeof(WCHAR);
4646 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4647 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4649 else
4651 size += 2*sizeof(WCHAR);
4652 data = msi_alloc(size);
4653 data[0] = 0x6;
4654 data[1] = 0;
4655 if (feature->Feature_Parent)
4656 strcpyW( &data[1], feature->Feature_Parent );
4657 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4658 (LPBYTE)data,size);
4659 msi_free(data);
4662 /* the UI chunk */
4663 uirow = MSI_CreateRecord( 1 );
4664 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4665 msi_ui_actiondata( package, szPublishFeatures, uirow );
4666 msiobj_release( &uirow->hdr );
4667 /* FIXME: call msi_ui_progress? */
4670 end:
4671 RegCloseKey(hkey);
4672 RegCloseKey(userdata);
4673 return rc;
4676 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4678 UINT r;
4679 HKEY hkey;
4680 MSIRECORD *uirow;
4682 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4684 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4685 &hkey, FALSE);
4686 if (r == ERROR_SUCCESS)
4688 RegDeleteValueW(hkey, feature->Feature);
4689 RegCloseKey(hkey);
4692 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4693 &hkey, FALSE);
4694 if (r == ERROR_SUCCESS)
4696 RegDeleteValueW(hkey, feature->Feature);
4697 RegCloseKey(hkey);
4700 uirow = MSI_CreateRecord( 1 );
4701 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4702 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4703 msiobj_release( &uirow->hdr );
4705 return ERROR_SUCCESS;
4708 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4710 MSIFEATURE *feature;
4712 if (!msi_check_unpublish(package))
4713 return ERROR_SUCCESS;
4715 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4717 msi_unpublish_feature(package, feature);
4720 return ERROR_SUCCESS;
4723 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4725 SYSTEMTIME systime;
4726 DWORD size, langid;
4727 WCHAR date[9], *val, *buffer;
4728 const WCHAR *prop, *key;
4730 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4731 static const WCHAR modpath_fmt[] =
4732 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4733 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4734 static const WCHAR szModifyPath[] =
4735 {'M','o','d','i','f','y','P','a','t','h',0};
4736 static const WCHAR szUninstallString[] =
4737 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4738 static const WCHAR szEstimatedSize[] =
4739 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4740 static const WCHAR szDisplayVersion[] =
4741 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4742 static const WCHAR szInstallSource[] =
4743 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4744 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4745 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4746 static const WCHAR szAuthorizedCDFPrefix[] =
4747 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4748 static const WCHAR szARPCONTACT[] =
4749 {'A','R','P','C','O','N','T','A','C','T',0};
4750 static const WCHAR szContact[] =
4751 {'C','o','n','t','a','c','t',0};
4752 static const WCHAR szARPCOMMENTS[] =
4753 {'A','R','P','C','O','M','M','E','N','T','S',0};
4754 static const WCHAR szComments[] =
4755 {'C','o','m','m','e','n','t','s',0};
4756 static const WCHAR szProductName[] =
4757 {'P','r','o','d','u','c','t','N','a','m','e',0};
4758 static const WCHAR szDisplayName[] =
4759 {'D','i','s','p','l','a','y','N','a','m','e',0};
4760 static const WCHAR szARPHELPLINK[] =
4761 {'A','R','P','H','E','L','P','L','I','N','K',0};
4762 static const WCHAR szHelpLink[] =
4763 {'H','e','l','p','L','i','n','k',0};
4764 static const WCHAR szARPHELPTELEPHONE[] =
4765 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4766 static const WCHAR szHelpTelephone[] =
4767 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4768 static const WCHAR szARPINSTALLLOCATION[] =
4769 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4770 static const WCHAR szInstallLocation[] =
4771 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4772 static const WCHAR szManufacturer[] =
4773 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4774 static const WCHAR szPublisher[] =
4775 {'P','u','b','l','i','s','h','e','r',0};
4776 static const WCHAR szARPREADME[] =
4777 {'A','R','P','R','E','A','D','M','E',0};
4778 static const WCHAR szReadme[] =
4779 {'R','e','a','d','M','e',0};
4780 static const WCHAR szARPSIZE[] =
4781 {'A','R','P','S','I','Z','E',0};
4782 static const WCHAR szSize[] =
4783 {'S','i','z','e',0};
4784 static const WCHAR szARPURLINFOABOUT[] =
4785 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4786 static const WCHAR szURLInfoAbout[] =
4787 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4788 static const WCHAR szARPURLUPDATEINFO[] =
4789 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4790 static const WCHAR szURLUpdateInfo[] =
4791 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4792 static const WCHAR szARPSYSTEMCOMPONENT[] =
4793 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4794 static const WCHAR szSystemComponent[] =
4795 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4797 static const WCHAR *propval[] = {
4798 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4799 szARPCONTACT, szContact,
4800 szARPCOMMENTS, szComments,
4801 szProductName, szDisplayName,
4802 szARPHELPLINK, szHelpLink,
4803 szARPHELPTELEPHONE, szHelpTelephone,
4804 szARPINSTALLLOCATION, szInstallLocation,
4805 szSourceDir, szInstallSource,
4806 szManufacturer, szPublisher,
4807 szARPREADME, szReadme,
4808 szARPSIZE, szSize,
4809 szARPURLINFOABOUT, szURLInfoAbout,
4810 szARPURLUPDATEINFO, szURLUpdateInfo,
4811 NULL
4813 const WCHAR **p = propval;
4815 while (*p)
4817 prop = *p++;
4818 key = *p++;
4819 val = msi_dup_property(package->db, prop);
4820 msi_reg_set_val_str(hkey, key, val);
4821 msi_free(val);
4824 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4825 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4827 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4829 size = deformat_string(package, modpath_fmt, &buffer);
4830 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4831 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4832 msi_free(buffer);
4834 /* FIXME: Write real Estimated Size when we have it */
4835 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4837 GetLocalTime(&systime);
4838 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4839 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4841 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4842 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4844 buffer = msi_dup_property(package->db, szProductVersion);
4845 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4846 if (buffer)
4848 DWORD verdword = msi_version_str_to_dword(buffer);
4850 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4851 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4852 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4853 msi_free(buffer);
4856 return ERROR_SUCCESS;
4859 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4861 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4862 MSIRECORD *uirow;
4863 LPWSTR upgrade_code;
4864 HKEY hkey, props, upgrade_key;
4865 UINT rc;
4867 /* FIXME: also need to publish if the product is in advertise mode */
4868 if (!msi_check_publish(package))
4869 return ERROR_SUCCESS;
4871 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4872 if (rc != ERROR_SUCCESS)
4873 return rc;
4875 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4876 if (rc != ERROR_SUCCESS)
4877 goto done;
4879 rc = msi_publish_install_properties(package, hkey);
4880 if (rc != ERROR_SUCCESS)
4881 goto done;
4883 rc = msi_publish_install_properties(package, props);
4884 if (rc != ERROR_SUCCESS)
4885 goto done;
4887 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4888 if (upgrade_code)
4890 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4891 if (rc == ERROR_SUCCESS)
4893 squash_guid( package->ProductCode, squashed_pc );
4894 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4895 RegCloseKey( upgrade_key );
4897 msi_free( upgrade_code );
4899 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4900 package->delete_on_close = FALSE;
4902 done:
4903 uirow = MSI_CreateRecord( 1 );
4904 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4905 msi_ui_actiondata( package, szRegisterProduct, uirow );
4906 msiobj_release( &uirow->hdr );
4908 RegCloseKey(hkey);
4909 return ERROR_SUCCESS;
4912 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4914 return execute_script(package,INSTALL_SCRIPT);
4917 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4919 MSIPACKAGE *package = param;
4920 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4921 WCHAR *p, *icon_path;
4923 if (!icon) return ERROR_SUCCESS;
4924 if ((icon_path = msi_build_icon_path( package, icon )))
4926 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4927 DeleteFileW( icon_path );
4928 if ((p = strrchrW( icon_path, '\\' )))
4930 *p = 0;
4931 RemoveDirectoryW( icon_path );
4933 msi_free( icon_path );
4935 return ERROR_SUCCESS;
4938 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4940 static const WCHAR query[]= {
4941 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4942 MSIQUERY *view;
4943 UINT r;
4945 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4946 if (r == ERROR_SUCCESS)
4948 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4949 msiobj_release( &view->hdr );
4950 if (r != ERROR_SUCCESS)
4951 return r;
4953 return ERROR_SUCCESS;
4956 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4958 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4959 WCHAR *upgrade, **features;
4960 BOOL full_uninstall = TRUE;
4961 MSIFEATURE *feature;
4962 MSIPATCHINFO *patch;
4963 UINT i;
4965 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4967 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4969 features = msi_split_string( remove, ',' );
4970 for (i = 0; features && features[i]; i++)
4972 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4974 msi_free(features);
4976 if (!full_uninstall)
4977 return ERROR_SUCCESS;
4979 MSIREG_DeleteProductKey(package->ProductCode);
4980 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4981 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4983 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4984 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4985 MSIREG_DeleteUserProductKey(package->ProductCode);
4986 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4988 upgrade = msi_dup_property(package->db, szUpgradeCode);
4989 if (upgrade)
4991 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4992 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4993 msi_free(upgrade);
4996 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4998 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4999 if (!strcmpW( package->ProductCode, patch->products ))
5001 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5002 patch->delete_on_close = TRUE;
5004 /* FIXME: remove local patch package if this is the last product */
5006 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5007 package->delete_on_close = TRUE;
5009 msi_unpublish_icons( package );
5010 return ERROR_SUCCESS;
5013 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5015 UINT rc;
5016 WCHAR *remove;
5018 /* turn off scheduling */
5019 package->script->CurrentlyScripting= FALSE;
5021 /* first do the same as an InstallExecute */
5022 rc = ACTION_InstallExecute(package);
5023 if (rc != ERROR_SUCCESS)
5024 return rc;
5026 /* then handle Commit Actions */
5027 rc = execute_script(package,COMMIT_SCRIPT);
5028 if (rc != ERROR_SUCCESS)
5029 return rc;
5031 remove = msi_dup_property(package->db, szRemove);
5032 rc = msi_unpublish_product(package, remove);
5033 msi_free(remove);
5034 return rc;
5037 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5039 static const WCHAR RunOnce[] = {
5040 'S','o','f','t','w','a','r','e','\\',
5041 'M','i','c','r','o','s','o','f','t','\\',
5042 'W','i','n','d','o','w','s','\\',
5043 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5044 'R','u','n','O','n','c','e',0};
5045 static const WCHAR InstallRunOnce[] = {
5046 'S','o','f','t','w','a','r','e','\\',
5047 'M','i','c','r','o','s','o','f','t','\\',
5048 'W','i','n','d','o','w','s','\\',
5049 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5050 'I','n','s','t','a','l','l','e','r','\\',
5051 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5053 static const WCHAR msiexec_fmt[] = {
5054 '%','s',
5055 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5056 '\"','%','s','\"',0};
5057 static const WCHAR install_fmt[] = {
5058 '/','I',' ','\"','%','s','\"',' ',
5059 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5060 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5061 WCHAR buffer[256], sysdir[MAX_PATH];
5062 HKEY hkey;
5063 WCHAR squished_pc[100];
5065 squash_guid(package->ProductCode,squished_pc);
5067 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5068 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5069 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5070 squished_pc);
5072 msi_reg_set_val_str( hkey, squished_pc, buffer );
5073 RegCloseKey(hkey);
5075 TRACE("Reboot command %s\n",debugstr_w(buffer));
5077 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5078 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5080 msi_reg_set_val_str( hkey, squished_pc, buffer );
5081 RegCloseKey(hkey);
5083 return ERROR_INSTALL_SUSPEND;
5086 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5088 static const WCHAR query[] =
5089 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5090 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5091 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5092 MSIRECORD *rec, *row;
5093 DWORD i, size = 0;
5094 va_list va;
5095 const WCHAR *str;
5096 WCHAR *data;
5098 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5100 rec = MSI_CreateRecord( count + 2 );
5101 str = MSI_RecordGetString( row, 1 );
5102 MSI_RecordSetStringW( rec, 0, str );
5103 msiobj_release( &row->hdr );
5104 MSI_RecordSetInteger( rec, 1, error );
5106 va_start( va, count );
5107 for (i = 0; i < count; i++)
5109 str = va_arg( va, const WCHAR *);
5110 MSI_RecordSetStringW( rec, i + 2, str );
5112 va_end( va );
5114 MSI_FormatRecordW( package, rec, NULL, &size );
5115 size++;
5116 data = msi_alloc( size * sizeof(WCHAR) );
5117 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5118 else data[0] = 0;
5119 msiobj_release( &rec->hdr );
5120 return data;
5123 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5125 DWORD attrib;
5126 UINT rc;
5129 * We are currently doing what should be done here in the top level Install
5130 * however for Administrative and uninstalls this step will be needed
5132 if (!package->PackagePath)
5133 return ERROR_SUCCESS;
5135 msi_set_sourcedir_props(package, TRUE);
5137 attrib = GetFileAttributesW(package->db->path);
5138 if (attrib == INVALID_FILE_ATTRIBUTES)
5140 LPWSTR prompt, msg;
5141 DWORD size = 0;
5143 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5144 package->Context, MSICODE_PRODUCT,
5145 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5146 if (rc == ERROR_MORE_DATA)
5148 prompt = msi_alloc(size * sizeof(WCHAR));
5149 MsiSourceListGetInfoW(package->ProductCode, NULL,
5150 package->Context, MSICODE_PRODUCT,
5151 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5153 else
5154 prompt = strdupW(package->db->path);
5156 msg = msi_build_error_string(package, 1302, 1, prompt);
5157 msi_free(prompt);
5158 while(attrib == INVALID_FILE_ATTRIBUTES)
5160 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5161 if (rc == IDCANCEL)
5163 msi_free(msg);
5164 return ERROR_INSTALL_USEREXIT;
5166 attrib = GetFileAttributesW(package->db->path);
5168 msi_free(msg);
5169 rc = ERROR_SUCCESS;
5171 else
5172 return ERROR_SUCCESS;
5174 return rc;
5177 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5179 HKEY hkey = 0;
5180 LPWSTR buffer, productid = NULL;
5181 UINT i, rc = ERROR_SUCCESS;
5182 MSIRECORD *uirow;
5184 static const WCHAR szPropKeys[][80] =
5186 {'P','r','o','d','u','c','t','I','D',0},
5187 {'U','S','E','R','N','A','M','E',0},
5188 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5189 {0},
5192 static const WCHAR szRegKeys[][80] =
5194 {'P','r','o','d','u','c','t','I','D',0},
5195 {'R','e','g','O','w','n','e','r',0},
5196 {'R','e','g','C','o','m','p','a','n','y',0},
5197 {0},
5200 if (msi_check_unpublish(package))
5202 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5203 goto end;
5206 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5207 if (!productid)
5208 goto end;
5210 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5211 NULL, &hkey, TRUE);
5212 if (rc != ERROR_SUCCESS)
5213 goto end;
5215 for( i = 0; szPropKeys[i][0]; i++ )
5217 buffer = msi_dup_property( package->db, szPropKeys[i] );
5218 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5219 msi_free( buffer );
5222 end:
5223 uirow = MSI_CreateRecord( 1 );
5224 MSI_RecordSetStringW( uirow, 1, productid );
5225 msi_ui_actiondata( package, szRegisterUser, uirow );
5226 msiobj_release( &uirow->hdr );
5228 msi_free(productid);
5229 RegCloseKey(hkey);
5230 return rc;
5234 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5236 UINT rc;
5238 package->script->InWhatSequence |= SEQUENCE_EXEC;
5239 rc = ACTION_ProcessExecSequence(package,FALSE);
5240 return rc;
5243 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5245 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5246 WCHAR productid_85[21], component_85[21], *ret;
5247 GUID clsid;
5248 DWORD sz;
5250 /* > is used if there is a component GUID and < if not. */
5252 productid_85[0] = 0;
5253 component_85[0] = 0;
5254 CLSIDFromString( package->ProductCode, &clsid );
5256 encode_base85_guid( &clsid, productid_85 );
5257 if (component)
5259 CLSIDFromString( component->ComponentId, &clsid );
5260 encode_base85_guid( &clsid, component_85 );
5263 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5264 debugstr_w(component_85));
5266 sz = 20 + strlenW( feature ) + 20 + 3;
5267 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5268 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5269 return ret;
5272 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5274 MSIPACKAGE *package = param;
5275 LPCWSTR compgroupid, component, feature, qualifier, text;
5276 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5277 HKEY hkey = NULL;
5278 UINT rc;
5279 MSICOMPONENT *comp;
5280 MSIFEATURE *feat;
5281 DWORD sz;
5282 MSIRECORD *uirow;
5283 int len;
5285 feature = MSI_RecordGetString(rec, 5);
5286 feat = msi_get_loaded_feature(package, feature);
5287 if (!feat)
5288 return ERROR_SUCCESS;
5290 feat->Action = msi_get_feature_action( package, feat );
5291 if (feat->Action != INSTALLSTATE_LOCAL &&
5292 feat->Action != INSTALLSTATE_SOURCE &&
5293 feat->Action != INSTALLSTATE_ADVERTISED)
5295 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5296 return ERROR_SUCCESS;
5299 component = MSI_RecordGetString(rec, 3);
5300 comp = msi_get_loaded_component(package, component);
5301 if (!comp)
5302 return ERROR_SUCCESS;
5304 compgroupid = MSI_RecordGetString(rec,1);
5305 qualifier = MSI_RecordGetString(rec,2);
5307 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5308 if (rc != ERROR_SUCCESS)
5309 goto end;
5311 advertise = msi_create_component_advertise_string( package, comp, feature );
5312 text = MSI_RecordGetString( rec, 4 );
5313 if (text)
5315 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5316 strcpyW( p, advertise );
5317 strcatW( p, text );
5318 msi_free( advertise );
5319 advertise = p;
5321 existing = msi_reg_get_val_str( hkey, qualifier );
5323 sz = strlenW( advertise ) + 1;
5324 if (existing)
5326 for (p = existing; *p; p += len)
5328 len = strlenW( p ) + 1;
5329 if (strcmpW( advertise, p )) sz += len;
5332 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5334 rc = ERROR_OUTOFMEMORY;
5335 goto end;
5337 q = output;
5338 if (existing)
5340 for (p = existing; *p; p += len)
5342 len = strlenW( p ) + 1;
5343 if (strcmpW( advertise, p ))
5345 memcpy( q, p, len * sizeof(WCHAR) );
5346 q += len;
5350 strcpyW( q, advertise );
5351 q[strlenW( q ) + 1] = 0;
5353 msi_reg_set_val_multi_str( hkey, qualifier, output );
5355 end:
5356 RegCloseKey(hkey);
5357 msi_free( output );
5358 msi_free( advertise );
5359 msi_free( existing );
5361 /* the UI chunk */
5362 uirow = MSI_CreateRecord( 2 );
5363 MSI_RecordSetStringW( uirow, 1, compgroupid );
5364 MSI_RecordSetStringW( uirow, 2, qualifier);
5365 msi_ui_actiondata( package, szPublishComponents, uirow );
5366 msiobj_release( &uirow->hdr );
5367 /* FIXME: call ui_progress? */
5369 return rc;
5373 * At present I am ignorning the advertised components part of this and only
5374 * focusing on the qualified component sets
5376 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5378 static const WCHAR query[] = {
5379 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5380 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5381 MSIQUERY *view;
5382 UINT rc;
5384 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5385 if (rc != ERROR_SUCCESS)
5386 return ERROR_SUCCESS;
5388 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5389 msiobj_release(&view->hdr);
5390 return rc;
5393 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5395 static const WCHAR szInstallerComponents[] = {
5396 'S','o','f','t','w','a','r','e','\\',
5397 'M','i','c','r','o','s','o','f','t','\\',
5398 'I','n','s','t','a','l','l','e','r','\\',
5399 'C','o','m','p','o','n','e','n','t','s','\\',0};
5401 MSIPACKAGE *package = param;
5402 LPCWSTR compgroupid, component, feature, qualifier;
5403 MSICOMPONENT *comp;
5404 MSIFEATURE *feat;
5405 MSIRECORD *uirow;
5406 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5407 LONG res;
5409 feature = MSI_RecordGetString( rec, 5 );
5410 feat = msi_get_loaded_feature( package, feature );
5411 if (!feat)
5412 return ERROR_SUCCESS;
5414 feat->Action = msi_get_feature_action( package, feat );
5415 if (feat->Action != INSTALLSTATE_ABSENT)
5417 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5418 return ERROR_SUCCESS;
5421 component = MSI_RecordGetString( rec, 3 );
5422 comp = msi_get_loaded_component( package, component );
5423 if (!comp)
5424 return ERROR_SUCCESS;
5426 compgroupid = MSI_RecordGetString( rec, 1 );
5427 qualifier = MSI_RecordGetString( rec, 2 );
5429 squash_guid( compgroupid, squashed );
5430 strcpyW( keypath, szInstallerComponents );
5431 strcatW( keypath, squashed );
5433 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5434 if (res != ERROR_SUCCESS)
5436 WARN("Unable to delete component key %d\n", res);
5439 uirow = MSI_CreateRecord( 2 );
5440 MSI_RecordSetStringW( uirow, 1, compgroupid );
5441 MSI_RecordSetStringW( uirow, 2, qualifier );
5442 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5443 msiobj_release( &uirow->hdr );
5445 return ERROR_SUCCESS;
5448 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5450 static const WCHAR query[] = {
5451 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5452 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5453 MSIQUERY *view;
5454 UINT rc;
5456 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5457 if (rc != ERROR_SUCCESS)
5458 return ERROR_SUCCESS;
5460 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5461 msiobj_release( &view->hdr );
5462 return rc;
5465 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5467 static const WCHAR query[] =
5468 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5469 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5470 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5471 MSIPACKAGE *package = param;
5472 MSICOMPONENT *component;
5473 MSIRECORD *row;
5474 MSIFILE *file;
5475 SC_HANDLE hscm = NULL, service = NULL;
5476 LPCWSTR comp, key;
5477 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5478 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5479 DWORD serv_type, start_type, err_control;
5480 SERVICE_DESCRIPTIONW sd = {NULL};
5482 comp = MSI_RecordGetString( rec, 12 );
5483 component = msi_get_loaded_component( package, comp );
5484 if (!component)
5486 WARN("service component not found\n");
5487 goto done;
5489 component->Action = msi_get_component_action( package, component );
5490 if (component->Action != INSTALLSTATE_LOCAL)
5492 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5493 goto done;
5495 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5496 if (!hscm)
5498 ERR("Failed to open the SC Manager!\n");
5499 goto done;
5502 start_type = MSI_RecordGetInteger(rec, 5);
5503 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5504 goto done;
5506 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5507 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5508 serv_type = MSI_RecordGetInteger(rec, 4);
5509 err_control = MSI_RecordGetInteger(rec, 6);
5510 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5511 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5512 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5513 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5514 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5515 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5517 /* fetch the service path */
5518 row = MSI_QueryGetRecord(package->db, query, comp);
5519 if (!row)
5521 ERR("Query failed\n");
5522 goto done;
5524 key = MSI_RecordGetString(row, 6);
5525 file = msi_get_loaded_file(package, key);
5526 msiobj_release(&row->hdr);
5527 if (!file)
5529 ERR("Failed to load the service file\n");
5530 goto done;
5533 if (!args || !args[0]) image_path = file->TargetPath;
5534 else
5536 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5537 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5538 return ERROR_OUTOFMEMORY;
5540 strcpyW(image_path, file->TargetPath);
5541 strcatW(image_path, szSpace);
5542 strcatW(image_path, args);
5544 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5545 start_type, err_control, image_path, load_order,
5546 NULL, depends, serv_name, pass);
5548 if (!service)
5550 if (GetLastError() != ERROR_SERVICE_EXISTS)
5551 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5553 else if (sd.lpDescription)
5555 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5556 WARN("failed to set service description %u\n", GetLastError());
5559 if (image_path != file->TargetPath) msi_free(image_path);
5560 done:
5561 CloseServiceHandle(service);
5562 CloseServiceHandle(hscm);
5563 msi_free(name);
5564 msi_free(disp);
5565 msi_free(sd.lpDescription);
5566 msi_free(load_order);
5567 msi_free(serv_name);
5568 msi_free(pass);
5569 msi_free(depends);
5570 msi_free(args);
5572 return ERROR_SUCCESS;
5575 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5577 static const WCHAR query[] = {
5578 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5579 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5580 MSIQUERY *view;
5581 UINT rc;
5583 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5584 if (rc != ERROR_SUCCESS)
5585 return ERROR_SUCCESS;
5587 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5588 msiobj_release(&view->hdr);
5589 return rc;
5592 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5593 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5595 LPCWSTR *vector, *temp_vector;
5596 LPWSTR p, q;
5597 DWORD sep_len;
5599 static const WCHAR separator[] = {'[','~',']',0};
5601 *numargs = 0;
5602 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5604 if (!args)
5605 return NULL;
5607 vector = msi_alloc(sizeof(LPWSTR));
5608 if (!vector)
5609 return NULL;
5611 p = args;
5614 (*numargs)++;
5615 vector[*numargs - 1] = p;
5617 if ((q = strstrW(p, separator)))
5619 *q = '\0';
5621 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5622 if (!temp_vector)
5624 msi_free(vector);
5625 return NULL;
5627 vector = temp_vector;
5629 p = q + sep_len;
5631 } while (q);
5633 return vector;
5636 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5638 MSIPACKAGE *package = param;
5639 MSICOMPONENT *comp;
5640 MSIRECORD *uirow;
5641 SC_HANDLE scm = NULL, service = NULL;
5642 LPCWSTR component, *vector = NULL;
5643 LPWSTR name, args, display_name = NULL;
5644 DWORD event, numargs, len, wait, dummy;
5645 UINT r = ERROR_FUNCTION_FAILED;
5646 SERVICE_STATUS_PROCESS status;
5647 ULONGLONG start_time;
5649 component = MSI_RecordGetString(rec, 6);
5650 comp = msi_get_loaded_component(package, component);
5651 if (!comp)
5652 return ERROR_SUCCESS;
5654 comp->Action = msi_get_component_action( package, comp );
5655 if (comp->Action != INSTALLSTATE_LOCAL)
5657 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5658 return ERROR_SUCCESS;
5661 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5662 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5663 event = MSI_RecordGetInteger(rec, 3);
5664 wait = MSI_RecordGetInteger(rec, 5);
5666 if (!(event & msidbServiceControlEventStart))
5668 r = ERROR_SUCCESS;
5669 goto done;
5672 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5673 if (!scm)
5675 ERR("Failed to open the service control manager\n");
5676 goto done;
5679 len = 0;
5680 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5681 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5683 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5684 GetServiceDisplayNameW( scm, name, display_name, &len );
5687 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5688 if (!service)
5690 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5691 goto done;
5694 vector = msi_service_args_to_vector(args, &numargs);
5696 if (!StartServiceW(service, numargs, vector) &&
5697 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5699 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5700 goto done;
5703 r = ERROR_SUCCESS;
5704 if (wait)
5706 /* wait for at most 30 seconds for the service to be up and running */
5707 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5708 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5710 TRACE("failed to query service status (%u)\n", GetLastError());
5711 goto done;
5713 start_time = GetTickCount64();
5714 while (status.dwCurrentState == SERVICE_START_PENDING)
5716 if (GetTickCount64() - start_time > 30000) break;
5717 Sleep(1000);
5718 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5719 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5721 TRACE("failed to query service status (%u)\n", GetLastError());
5722 goto done;
5725 if (status.dwCurrentState != SERVICE_RUNNING)
5727 WARN("service failed to start %u\n", status.dwCurrentState);
5728 r = ERROR_FUNCTION_FAILED;
5732 done:
5733 uirow = MSI_CreateRecord( 2 );
5734 MSI_RecordSetStringW( uirow, 1, display_name );
5735 MSI_RecordSetStringW( uirow, 2, name );
5736 msi_ui_actiondata( package, szStartServices, uirow );
5737 msiobj_release( &uirow->hdr );
5739 CloseServiceHandle(service);
5740 CloseServiceHandle(scm);
5742 msi_free(name);
5743 msi_free(args);
5744 msi_free(vector);
5745 msi_free(display_name);
5746 return r;
5749 static UINT ACTION_StartServices( MSIPACKAGE *package )
5751 static const WCHAR query[] = {
5752 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5753 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5754 MSIQUERY *view;
5755 UINT rc;
5757 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5758 if (rc != ERROR_SUCCESS)
5759 return ERROR_SUCCESS;
5761 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5762 msiobj_release(&view->hdr);
5763 return rc;
5766 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5768 DWORD i, needed, count;
5769 ENUM_SERVICE_STATUSW *dependencies;
5770 SERVICE_STATUS ss;
5771 SC_HANDLE depserv;
5773 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5774 0, &needed, &count))
5775 return TRUE;
5777 if (GetLastError() != ERROR_MORE_DATA)
5778 return FALSE;
5780 dependencies = msi_alloc(needed);
5781 if (!dependencies)
5782 return FALSE;
5784 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5785 needed, &needed, &count))
5786 goto error;
5788 for (i = 0; i < count; i++)
5790 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5791 SERVICE_STOP | SERVICE_QUERY_STATUS);
5792 if (!depserv)
5793 goto error;
5795 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5796 goto error;
5799 return TRUE;
5801 error:
5802 msi_free(dependencies);
5803 return FALSE;
5806 static UINT stop_service( LPCWSTR name )
5808 SC_HANDLE scm = NULL, service = NULL;
5809 SERVICE_STATUS status;
5810 SERVICE_STATUS_PROCESS ssp;
5811 DWORD needed;
5813 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5814 if (!scm)
5816 WARN("Failed to open the SCM: %d\n", GetLastError());
5817 goto done;
5820 service = OpenServiceW(scm, name,
5821 SERVICE_STOP |
5822 SERVICE_QUERY_STATUS |
5823 SERVICE_ENUMERATE_DEPENDENTS);
5824 if (!service)
5826 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5827 goto done;
5830 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5831 sizeof(SERVICE_STATUS_PROCESS), &needed))
5833 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5834 goto done;
5837 if (ssp.dwCurrentState == SERVICE_STOPPED)
5838 goto done;
5840 stop_service_dependents(scm, service);
5842 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5843 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5845 done:
5846 CloseServiceHandle(service);
5847 CloseServiceHandle(scm);
5849 return ERROR_SUCCESS;
5852 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5854 MSIPACKAGE *package = param;
5855 MSICOMPONENT *comp;
5856 MSIRECORD *uirow;
5857 LPCWSTR component;
5858 LPWSTR name = NULL, display_name = NULL;
5859 DWORD event, len;
5860 SC_HANDLE scm;
5862 event = MSI_RecordGetInteger( rec, 3 );
5863 if (!(event & msidbServiceControlEventStop))
5864 return ERROR_SUCCESS;
5866 component = MSI_RecordGetString( rec, 6 );
5867 comp = msi_get_loaded_component( package, component );
5868 if (!comp)
5869 return ERROR_SUCCESS;
5871 comp->Action = msi_get_component_action( package, comp );
5872 if (comp->Action != INSTALLSTATE_ABSENT)
5874 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5875 return ERROR_SUCCESS;
5878 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5879 if (!scm)
5881 ERR("Failed to open the service control manager\n");
5882 goto done;
5885 len = 0;
5886 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5887 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5889 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5890 GetServiceDisplayNameW( scm, name, display_name, &len );
5892 CloseServiceHandle( scm );
5894 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5895 stop_service( name );
5897 done:
5898 uirow = MSI_CreateRecord( 2 );
5899 MSI_RecordSetStringW( uirow, 1, display_name );
5900 MSI_RecordSetStringW( uirow, 2, name );
5901 msi_ui_actiondata( package, szStopServices, uirow );
5902 msiobj_release( &uirow->hdr );
5904 msi_free( name );
5905 msi_free( display_name );
5906 return ERROR_SUCCESS;
5909 static UINT ACTION_StopServices( MSIPACKAGE *package )
5911 static const WCHAR query[] = {
5912 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5913 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5914 MSIQUERY *view;
5915 UINT rc;
5917 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5918 if (rc != ERROR_SUCCESS)
5919 return ERROR_SUCCESS;
5921 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5922 msiobj_release(&view->hdr);
5923 return rc;
5926 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5928 MSIPACKAGE *package = param;
5929 MSICOMPONENT *comp;
5930 MSIRECORD *uirow;
5931 LPCWSTR component;
5932 LPWSTR name = NULL, display_name = NULL;
5933 DWORD event, len;
5934 SC_HANDLE scm = NULL, service = NULL;
5936 event = MSI_RecordGetInteger( rec, 3 );
5937 if (!(event & msidbServiceControlEventDelete))
5938 return ERROR_SUCCESS;
5940 component = MSI_RecordGetString(rec, 6);
5941 comp = msi_get_loaded_component(package, component);
5942 if (!comp)
5943 return ERROR_SUCCESS;
5945 comp->Action = msi_get_component_action( package, comp );
5946 if (comp->Action != INSTALLSTATE_ABSENT)
5948 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5949 return ERROR_SUCCESS;
5952 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5953 stop_service( name );
5955 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5956 if (!scm)
5958 WARN("Failed to open the SCM: %d\n", GetLastError());
5959 goto done;
5962 len = 0;
5963 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5964 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5966 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5967 GetServiceDisplayNameW( scm, name, display_name, &len );
5970 service = OpenServiceW( scm, name, DELETE );
5971 if (!service)
5973 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5974 goto done;
5977 if (!DeleteService( service ))
5978 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5980 done:
5981 uirow = MSI_CreateRecord( 2 );
5982 MSI_RecordSetStringW( uirow, 1, display_name );
5983 MSI_RecordSetStringW( uirow, 2, name );
5984 msi_ui_actiondata( package, szDeleteServices, uirow );
5985 msiobj_release( &uirow->hdr );
5987 CloseServiceHandle( service );
5988 CloseServiceHandle( scm );
5989 msi_free( name );
5990 msi_free( display_name );
5992 return ERROR_SUCCESS;
5995 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5997 static const WCHAR query[] = {
5998 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5999 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6000 MSIQUERY *view;
6001 UINT rc;
6003 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6004 if (rc != ERROR_SUCCESS)
6005 return ERROR_SUCCESS;
6007 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6008 msiobj_release( &view->hdr );
6009 return rc;
6012 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6014 MSIPACKAGE *package = param;
6015 LPWSTR driver, driver_path, ptr;
6016 WCHAR outpath[MAX_PATH];
6017 MSIFILE *driver_file = NULL, *setup_file = NULL;
6018 MSICOMPONENT *comp;
6019 MSIRECORD *uirow;
6020 LPCWSTR desc, file_key, component;
6021 DWORD len, usage;
6022 UINT r = ERROR_SUCCESS;
6024 static const WCHAR driver_fmt[] = {
6025 'D','r','i','v','e','r','=','%','s',0};
6026 static const WCHAR setup_fmt[] = {
6027 'S','e','t','u','p','=','%','s',0};
6028 static const WCHAR usage_fmt[] = {
6029 'F','i','l','e','U','s','a','g','e','=','1',0};
6031 component = MSI_RecordGetString( rec, 2 );
6032 comp = msi_get_loaded_component( package, component );
6033 if (!comp)
6034 return ERROR_SUCCESS;
6036 comp->Action = msi_get_component_action( package, comp );
6037 if (comp->Action != INSTALLSTATE_LOCAL)
6039 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6040 return ERROR_SUCCESS;
6042 desc = MSI_RecordGetString(rec, 3);
6044 file_key = MSI_RecordGetString( rec, 4 );
6045 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6047 file_key = MSI_RecordGetString( rec, 5 );
6048 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6050 if (!driver_file)
6052 ERR("ODBC Driver entry not found!\n");
6053 return ERROR_FUNCTION_FAILED;
6056 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6057 if (setup_file)
6058 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6059 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6061 driver = msi_alloc(len * sizeof(WCHAR));
6062 if (!driver)
6063 return ERROR_OUTOFMEMORY;
6065 ptr = driver;
6066 lstrcpyW(ptr, desc);
6067 ptr += lstrlenW(ptr) + 1;
6069 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6070 ptr += len + 1;
6072 if (setup_file)
6074 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6075 ptr += len + 1;
6078 lstrcpyW(ptr, usage_fmt);
6079 ptr += lstrlenW(ptr) + 1;
6080 *ptr = '\0';
6082 driver_path = strdupW(driver_file->TargetPath);
6083 ptr = strrchrW(driver_path, '\\');
6084 if (ptr) *ptr = '\0';
6086 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6087 NULL, ODBC_INSTALL_COMPLETE, &usage))
6089 ERR("Failed to install SQL driver!\n");
6090 r = ERROR_FUNCTION_FAILED;
6093 uirow = MSI_CreateRecord( 5 );
6094 MSI_RecordSetStringW( uirow, 1, desc );
6095 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6096 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6097 msi_ui_actiondata( package, szInstallODBC, uirow );
6098 msiobj_release( &uirow->hdr );
6100 msi_free(driver);
6101 msi_free(driver_path);
6103 return r;
6106 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6108 MSIPACKAGE *package = param;
6109 LPWSTR translator, translator_path, ptr;
6110 WCHAR outpath[MAX_PATH];
6111 MSIFILE *translator_file = NULL, *setup_file = NULL;
6112 MSICOMPONENT *comp;
6113 MSIRECORD *uirow;
6114 LPCWSTR desc, file_key, component;
6115 DWORD len, usage;
6116 UINT r = ERROR_SUCCESS;
6118 static const WCHAR translator_fmt[] = {
6119 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6120 static const WCHAR setup_fmt[] = {
6121 'S','e','t','u','p','=','%','s',0};
6123 component = MSI_RecordGetString( rec, 2 );
6124 comp = msi_get_loaded_component( package, component );
6125 if (!comp)
6126 return ERROR_SUCCESS;
6128 comp->Action = msi_get_component_action( package, comp );
6129 if (comp->Action != INSTALLSTATE_LOCAL)
6131 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6132 return ERROR_SUCCESS;
6134 desc = MSI_RecordGetString(rec, 3);
6136 file_key = MSI_RecordGetString( rec, 4 );
6137 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6139 file_key = MSI_RecordGetString( rec, 5 );
6140 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6142 if (!translator_file)
6144 ERR("ODBC Translator entry not found!\n");
6145 return ERROR_FUNCTION_FAILED;
6148 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6149 if (setup_file)
6150 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6152 translator = msi_alloc(len * sizeof(WCHAR));
6153 if (!translator)
6154 return ERROR_OUTOFMEMORY;
6156 ptr = translator;
6157 lstrcpyW(ptr, desc);
6158 ptr += lstrlenW(ptr) + 1;
6160 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6161 ptr += len + 1;
6163 if (setup_file)
6165 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6166 ptr += len + 1;
6168 *ptr = '\0';
6170 translator_path = strdupW(translator_file->TargetPath);
6171 ptr = strrchrW(translator_path, '\\');
6172 if (ptr) *ptr = '\0';
6174 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6175 NULL, ODBC_INSTALL_COMPLETE, &usage))
6177 ERR("Failed to install SQL translator!\n");
6178 r = ERROR_FUNCTION_FAILED;
6181 uirow = MSI_CreateRecord( 5 );
6182 MSI_RecordSetStringW( uirow, 1, desc );
6183 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6184 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6185 msi_ui_actiondata( package, szInstallODBC, uirow );
6186 msiobj_release( &uirow->hdr );
6188 msi_free(translator);
6189 msi_free(translator_path);
6191 return r;
6194 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6196 MSIPACKAGE *package = param;
6197 MSICOMPONENT *comp;
6198 LPWSTR attrs;
6199 LPCWSTR desc, driver, component;
6200 WORD request = ODBC_ADD_SYS_DSN;
6201 INT registration;
6202 DWORD len;
6203 UINT r = ERROR_SUCCESS;
6204 MSIRECORD *uirow;
6206 static const WCHAR attrs_fmt[] = {
6207 'D','S','N','=','%','s',0 };
6209 component = MSI_RecordGetString( rec, 2 );
6210 comp = msi_get_loaded_component( package, component );
6211 if (!comp)
6212 return ERROR_SUCCESS;
6214 comp->Action = msi_get_component_action( package, comp );
6215 if (comp->Action != INSTALLSTATE_LOCAL)
6217 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6218 return ERROR_SUCCESS;
6221 desc = MSI_RecordGetString(rec, 3);
6222 driver = MSI_RecordGetString(rec, 4);
6223 registration = MSI_RecordGetInteger(rec, 5);
6225 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6226 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6228 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6229 attrs = msi_alloc(len * sizeof(WCHAR));
6230 if (!attrs)
6231 return ERROR_OUTOFMEMORY;
6233 len = sprintfW(attrs, attrs_fmt, desc);
6234 attrs[len + 1] = 0;
6236 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6238 ERR("Failed to install SQL data source!\n");
6239 r = ERROR_FUNCTION_FAILED;
6242 uirow = MSI_CreateRecord( 5 );
6243 MSI_RecordSetStringW( uirow, 1, desc );
6244 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6245 MSI_RecordSetInteger( uirow, 3, request );
6246 msi_ui_actiondata( package, szInstallODBC, uirow );
6247 msiobj_release( &uirow->hdr );
6249 msi_free(attrs);
6251 return r;
6254 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6256 static const WCHAR driver_query[] = {
6257 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6258 'O','D','B','C','D','r','i','v','e','r',0};
6259 static const WCHAR translator_query[] = {
6260 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6261 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6262 static const WCHAR source_query[] = {
6263 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6264 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6265 MSIQUERY *view;
6266 UINT rc;
6268 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6269 if (rc == ERROR_SUCCESS)
6271 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6272 msiobj_release(&view->hdr);
6273 if (rc != ERROR_SUCCESS)
6274 return rc;
6276 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6277 if (rc == ERROR_SUCCESS)
6279 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6280 msiobj_release(&view->hdr);
6281 if (rc != ERROR_SUCCESS)
6282 return rc;
6284 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6285 if (rc == ERROR_SUCCESS)
6287 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6288 msiobj_release(&view->hdr);
6289 if (rc != ERROR_SUCCESS)
6290 return rc;
6292 return ERROR_SUCCESS;
6295 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6297 MSIPACKAGE *package = param;
6298 MSICOMPONENT *comp;
6299 MSIRECORD *uirow;
6300 DWORD usage;
6301 LPCWSTR desc, component;
6303 component = MSI_RecordGetString( rec, 2 );
6304 comp = msi_get_loaded_component( package, component );
6305 if (!comp)
6306 return ERROR_SUCCESS;
6308 comp->Action = msi_get_component_action( package, comp );
6309 if (comp->Action != INSTALLSTATE_ABSENT)
6311 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6312 return ERROR_SUCCESS;
6315 desc = MSI_RecordGetString( rec, 3 );
6316 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6318 WARN("Failed to remove ODBC driver\n");
6320 else if (!usage)
6322 FIXME("Usage count reached 0\n");
6325 uirow = MSI_CreateRecord( 2 );
6326 MSI_RecordSetStringW( uirow, 1, desc );
6327 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6328 msi_ui_actiondata( package, szRemoveODBC, uirow );
6329 msiobj_release( &uirow->hdr );
6331 return ERROR_SUCCESS;
6334 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6336 MSIPACKAGE *package = param;
6337 MSICOMPONENT *comp;
6338 MSIRECORD *uirow;
6339 DWORD usage;
6340 LPCWSTR desc, component;
6342 component = MSI_RecordGetString( rec, 2 );
6343 comp = msi_get_loaded_component( package, component );
6344 if (!comp)
6345 return ERROR_SUCCESS;
6347 comp->Action = msi_get_component_action( package, comp );
6348 if (comp->Action != INSTALLSTATE_ABSENT)
6350 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6351 return ERROR_SUCCESS;
6354 desc = MSI_RecordGetString( rec, 3 );
6355 if (!SQLRemoveTranslatorW( desc, &usage ))
6357 WARN("Failed to remove ODBC translator\n");
6359 else if (!usage)
6361 FIXME("Usage count reached 0\n");
6364 uirow = MSI_CreateRecord( 2 );
6365 MSI_RecordSetStringW( uirow, 1, desc );
6366 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6367 msi_ui_actiondata( package, szRemoveODBC, uirow );
6368 msiobj_release( &uirow->hdr );
6370 return ERROR_SUCCESS;
6373 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6375 MSIPACKAGE *package = param;
6376 MSICOMPONENT *comp;
6377 MSIRECORD *uirow;
6378 LPWSTR attrs;
6379 LPCWSTR desc, driver, component;
6380 WORD request = ODBC_REMOVE_SYS_DSN;
6381 INT registration;
6382 DWORD len;
6384 static const WCHAR attrs_fmt[] = {
6385 'D','S','N','=','%','s',0 };
6387 component = MSI_RecordGetString( rec, 2 );
6388 comp = msi_get_loaded_component( package, component );
6389 if (!comp)
6390 return ERROR_SUCCESS;
6392 comp->Action = msi_get_component_action( package, comp );
6393 if (comp->Action != INSTALLSTATE_ABSENT)
6395 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6396 return ERROR_SUCCESS;
6399 desc = MSI_RecordGetString( rec, 3 );
6400 driver = MSI_RecordGetString( rec, 4 );
6401 registration = MSI_RecordGetInteger( rec, 5 );
6403 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6404 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6406 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6407 attrs = msi_alloc( len * sizeof(WCHAR) );
6408 if (!attrs)
6409 return ERROR_OUTOFMEMORY;
6411 FIXME("Use ODBCSourceAttribute table\n");
6413 len = sprintfW( attrs, attrs_fmt, desc );
6414 attrs[len + 1] = 0;
6416 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6418 WARN("Failed to remove ODBC data source\n");
6420 msi_free( attrs );
6422 uirow = MSI_CreateRecord( 3 );
6423 MSI_RecordSetStringW( uirow, 1, desc );
6424 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6425 MSI_RecordSetInteger( uirow, 3, request );
6426 msi_ui_actiondata( package, szRemoveODBC, uirow );
6427 msiobj_release( &uirow->hdr );
6429 return ERROR_SUCCESS;
6432 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6434 static const WCHAR driver_query[] = {
6435 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6436 'O','D','B','C','D','r','i','v','e','r',0};
6437 static const WCHAR translator_query[] = {
6438 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6439 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6440 static const WCHAR source_query[] = {
6441 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6442 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6443 MSIQUERY *view;
6444 UINT rc;
6446 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6447 if (rc == ERROR_SUCCESS)
6449 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6450 msiobj_release( &view->hdr );
6451 if (rc != ERROR_SUCCESS)
6452 return rc;
6454 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6455 if (rc == ERROR_SUCCESS)
6457 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6458 msiobj_release( &view->hdr );
6459 if (rc != ERROR_SUCCESS)
6460 return rc;
6462 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6463 if (rc == ERROR_SUCCESS)
6465 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6466 msiobj_release( &view->hdr );
6467 if (rc != ERROR_SUCCESS)
6468 return rc;
6470 return ERROR_SUCCESS;
6473 #define ENV_ACT_SETALWAYS 0x1
6474 #define ENV_ACT_SETABSENT 0x2
6475 #define ENV_ACT_REMOVE 0x4
6476 #define ENV_ACT_REMOVEMATCH 0x8
6478 #define ENV_MOD_MACHINE 0x20000000
6479 #define ENV_MOD_APPEND 0x40000000
6480 #define ENV_MOD_PREFIX 0x80000000
6481 #define ENV_MOD_MASK 0xC0000000
6483 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6485 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6487 LPCWSTR cptr = *name;
6489 static const WCHAR prefix[] = {'[','~',']',0};
6490 static const int prefix_len = 3;
6492 *flags = 0;
6493 while (*cptr)
6495 if (*cptr == '=')
6496 *flags |= ENV_ACT_SETALWAYS;
6497 else if (*cptr == '+')
6498 *flags |= ENV_ACT_SETABSENT;
6499 else if (*cptr == '-')
6500 *flags |= ENV_ACT_REMOVE;
6501 else if (*cptr == '!')
6502 *flags |= ENV_ACT_REMOVEMATCH;
6503 else if (*cptr == '*')
6504 *flags |= ENV_MOD_MACHINE;
6505 else
6506 break;
6508 cptr++;
6509 (*name)++;
6512 if (!*cptr)
6514 ERR("Missing environment variable\n");
6515 return ERROR_FUNCTION_FAILED;
6518 if (*value)
6520 LPCWSTR ptr = *value;
6521 if (!strncmpW(ptr, prefix, prefix_len))
6523 if (ptr[prefix_len] == szSemiColon[0])
6525 *flags |= ENV_MOD_APPEND;
6526 *value += lstrlenW(prefix);
6528 else
6530 *value = NULL;
6533 else if (lstrlenW(*value) >= prefix_len)
6535 ptr += lstrlenW(ptr) - prefix_len;
6536 if (!strcmpW( ptr, prefix ))
6538 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6540 *flags |= ENV_MOD_PREFIX;
6541 /* the "[~]" will be removed by deformat_string */;
6543 else
6545 *value = NULL;
6551 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6552 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6553 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6554 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6556 ERR("Invalid flags: %08x\n", *flags);
6557 return ERROR_FUNCTION_FAILED;
6560 if (!*flags)
6561 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6563 return ERROR_SUCCESS;
6566 static UINT open_env_key( DWORD flags, HKEY *key )
6568 static const WCHAR user_env[] =
6569 {'E','n','v','i','r','o','n','m','e','n','t',0};
6570 static const WCHAR machine_env[] =
6571 {'S','y','s','t','e','m','\\',
6572 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6573 'C','o','n','t','r','o','l','\\',
6574 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6575 'E','n','v','i','r','o','n','m','e','n','t',0};
6576 const WCHAR *env;
6577 HKEY root;
6578 LONG res;
6580 if (flags & ENV_MOD_MACHINE)
6582 env = machine_env;
6583 root = HKEY_LOCAL_MACHINE;
6585 else
6587 env = user_env;
6588 root = HKEY_CURRENT_USER;
6591 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6592 if (res != ERROR_SUCCESS)
6594 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6595 return ERROR_FUNCTION_FAILED;
6598 return ERROR_SUCCESS;
6601 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6603 MSIPACKAGE *package = param;
6604 LPCWSTR name, value, component;
6605 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6606 DWORD flags, type, size;
6607 UINT res;
6608 HKEY env = NULL;
6609 MSICOMPONENT *comp;
6610 MSIRECORD *uirow;
6611 int action = 0;
6613 component = MSI_RecordGetString(rec, 4);
6614 comp = msi_get_loaded_component(package, component);
6615 if (!comp)
6616 return ERROR_SUCCESS;
6618 comp->Action = msi_get_component_action( package, comp );
6619 if (comp->Action != INSTALLSTATE_LOCAL)
6621 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6622 return ERROR_SUCCESS;
6624 name = MSI_RecordGetString(rec, 2);
6625 value = MSI_RecordGetString(rec, 3);
6627 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6629 res = env_parse_flags(&name, &value, &flags);
6630 if (res != ERROR_SUCCESS || !value)
6631 goto done;
6633 if (value && !deformat_string(package, value, &deformatted))
6635 res = ERROR_OUTOFMEMORY;
6636 goto done;
6639 value = deformatted;
6641 res = open_env_key( flags, &env );
6642 if (res != ERROR_SUCCESS)
6643 goto done;
6645 if (flags & ENV_MOD_MACHINE)
6646 action |= 0x20000000;
6648 size = 0;
6649 type = REG_SZ;
6650 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6651 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6652 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6653 goto done;
6655 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6657 action = 0x2;
6659 /* Nothing to do. */
6660 if (!value)
6662 res = ERROR_SUCCESS;
6663 goto done;
6666 /* If we are appending but the string was empty, strip ; */
6667 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6669 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6670 newval = strdupW(value);
6671 if (!newval)
6673 res = ERROR_OUTOFMEMORY;
6674 goto done;
6677 else
6679 action = 0x1;
6681 /* Contrary to MSDN, +-variable to [~];path works */
6682 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6684 res = ERROR_SUCCESS;
6685 goto done;
6688 data = msi_alloc(size);
6689 if (!data)
6691 RegCloseKey(env);
6692 return ERROR_OUTOFMEMORY;
6695 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6696 if (res != ERROR_SUCCESS)
6697 goto done;
6699 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6701 action = 0x4;
6702 res = RegDeleteValueW(env, name);
6703 if (res != ERROR_SUCCESS)
6704 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6705 goto done;
6708 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6709 if (flags & ENV_MOD_MASK)
6711 DWORD mod_size;
6712 int multiplier = 0;
6713 if (flags & ENV_MOD_APPEND) multiplier++;
6714 if (flags & ENV_MOD_PREFIX) multiplier++;
6715 mod_size = lstrlenW(value) * multiplier;
6716 size += mod_size * sizeof(WCHAR);
6719 newval = msi_alloc(size);
6720 ptr = newval;
6721 if (!newval)
6723 res = ERROR_OUTOFMEMORY;
6724 goto done;
6727 if (flags & ENV_MOD_PREFIX)
6729 lstrcpyW(newval, value);
6730 ptr = newval + lstrlenW(value);
6731 action |= 0x80000000;
6734 lstrcpyW(ptr, data);
6736 if (flags & ENV_MOD_APPEND)
6738 lstrcatW(newval, value);
6739 action |= 0x40000000;
6742 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6743 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6744 if (res)
6746 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6749 done:
6750 uirow = MSI_CreateRecord( 3 );
6751 MSI_RecordSetStringW( uirow, 1, name );
6752 MSI_RecordSetStringW( uirow, 2, newval );
6753 MSI_RecordSetInteger( uirow, 3, action );
6754 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6755 msiobj_release( &uirow->hdr );
6757 if (env) RegCloseKey(env);
6758 msi_free(deformatted);
6759 msi_free(data);
6760 msi_free(newval);
6761 return res;
6764 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6766 static const WCHAR query[] = {
6767 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6768 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6769 MSIQUERY *view;
6770 UINT rc;
6772 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6773 if (rc != ERROR_SUCCESS)
6774 return ERROR_SUCCESS;
6776 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6777 msiobj_release(&view->hdr);
6778 return rc;
6781 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6783 MSIPACKAGE *package = param;
6784 LPCWSTR name, value, component;
6785 LPWSTR deformatted = NULL;
6786 DWORD flags;
6787 HKEY env;
6788 MSICOMPONENT *comp;
6789 MSIRECORD *uirow;
6790 int action = 0;
6791 LONG res;
6792 UINT r;
6794 component = MSI_RecordGetString( rec, 4 );
6795 comp = msi_get_loaded_component( package, component );
6796 if (!comp)
6797 return ERROR_SUCCESS;
6799 comp->Action = msi_get_component_action( package, comp );
6800 if (comp->Action != INSTALLSTATE_ABSENT)
6802 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6803 return ERROR_SUCCESS;
6805 name = MSI_RecordGetString( rec, 2 );
6806 value = MSI_RecordGetString( rec, 3 );
6808 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6810 r = env_parse_flags( &name, &value, &flags );
6811 if (r != ERROR_SUCCESS)
6812 return r;
6814 if (!(flags & ENV_ACT_REMOVE))
6816 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6817 return ERROR_SUCCESS;
6820 if (value && !deformat_string( package, value, &deformatted ))
6821 return ERROR_OUTOFMEMORY;
6823 value = deformatted;
6825 r = open_env_key( flags, &env );
6826 if (r != ERROR_SUCCESS)
6828 r = ERROR_SUCCESS;
6829 goto done;
6832 if (flags & ENV_MOD_MACHINE)
6833 action |= 0x20000000;
6835 TRACE("Removing %s\n", debugstr_w(name));
6837 res = RegDeleteValueW( env, name );
6838 if (res != ERROR_SUCCESS)
6840 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6841 r = ERROR_SUCCESS;
6844 done:
6845 uirow = MSI_CreateRecord( 3 );
6846 MSI_RecordSetStringW( uirow, 1, name );
6847 MSI_RecordSetStringW( uirow, 2, value );
6848 MSI_RecordSetInteger( uirow, 3, action );
6849 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6850 msiobj_release( &uirow->hdr );
6852 if (env) RegCloseKey( env );
6853 msi_free( deformatted );
6854 return r;
6857 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6859 static const WCHAR query[] = {
6860 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6861 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6862 MSIQUERY *view;
6863 UINT rc;
6865 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6866 if (rc != ERROR_SUCCESS)
6867 return ERROR_SUCCESS;
6869 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6870 msiobj_release( &view->hdr );
6871 return rc;
6874 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6876 LPWSTR key, template, id;
6877 UINT r = ERROR_SUCCESS;
6879 id = msi_dup_property( package->db, szProductID );
6880 if (id)
6882 msi_free( id );
6883 return ERROR_SUCCESS;
6885 template = msi_dup_property( package->db, szPIDTemplate );
6886 key = msi_dup_property( package->db, szPIDKEY );
6888 if (key && template)
6890 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6891 r = msi_set_property( package->db, szProductID, key );
6893 msi_free( template );
6894 msi_free( key );
6895 return r;
6898 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6900 TRACE("\n");
6901 package->need_reboot = 1;
6902 return ERROR_SUCCESS;
6905 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6907 static const WCHAR szAvailableFreeReg[] =
6908 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6909 MSIRECORD *uirow;
6910 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6912 TRACE("%p %d kilobytes\n", package, space);
6914 uirow = MSI_CreateRecord( 1 );
6915 MSI_RecordSetInteger( uirow, 1, space );
6916 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6917 msiobj_release( &uirow->hdr );
6919 return ERROR_SUCCESS;
6922 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6924 TRACE("%p\n", package);
6926 msi_set_property( package->db, szRollbackDisabled, szOne );
6927 return ERROR_SUCCESS;
6930 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6932 FIXME("%p\n", package);
6933 return ERROR_SUCCESS;
6936 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6938 static const WCHAR driver_query[] = {
6939 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6940 'O','D','B','C','D','r','i','v','e','r',0};
6941 static const WCHAR translator_query[] = {
6942 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6943 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6944 MSIQUERY *view;
6945 UINT r, count;
6947 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6948 if (r == ERROR_SUCCESS)
6950 count = 0;
6951 r = MSI_IterateRecords( view, &count, NULL, package );
6952 msiobj_release( &view->hdr );
6953 if (r != ERROR_SUCCESS)
6954 return r;
6955 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6957 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6958 if (r == ERROR_SUCCESS)
6960 count = 0;
6961 r = MSI_IterateRecords( view, &count, NULL, package );
6962 msiobj_release( &view->hdr );
6963 if (r != ERROR_SUCCESS)
6964 return r;
6965 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6967 return ERROR_SUCCESS;
6970 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6972 MSIPACKAGE *package = param;
6973 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6974 WCHAR *value;
6976 if ((value = msi_dup_property( package->db, property )))
6978 FIXME("remove %s\n", debugstr_w(value));
6979 msi_free( value );
6981 return ERROR_SUCCESS;
6984 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6986 static const WCHAR query[] = {
6987 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6988 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6989 MSIQUERY *view;
6990 UINT r;
6992 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6993 if (r == ERROR_SUCCESS)
6995 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6996 msiobj_release( &view->hdr );
6997 if (r != ERROR_SUCCESS)
6998 return r;
7000 return ERROR_SUCCESS;
7003 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7005 MSIPACKAGE *package = param;
7006 int attributes = MSI_RecordGetInteger( rec, 5 );
7008 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7010 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7011 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7012 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7013 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7014 HKEY hkey;
7015 UINT r;
7017 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7019 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7020 if (r != ERROR_SUCCESS)
7021 return ERROR_SUCCESS;
7023 else
7025 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7026 if (r != ERROR_SUCCESS)
7027 return ERROR_SUCCESS;
7029 RegCloseKey( hkey );
7031 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7032 debugstr_w(upgrade_code), debugstr_w(version_min),
7033 debugstr_w(version_max), debugstr_w(language));
7035 return ERROR_SUCCESS;
7038 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7040 static const WCHAR query[] = {
7041 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7042 'U','p','g','r','a','d','e',0};
7043 MSIQUERY *view;
7044 UINT r;
7046 if (msi_get_property_int( package->db, szInstalled, 0 ))
7048 TRACE("product is installed, skipping action\n");
7049 return ERROR_SUCCESS;
7051 if (msi_get_property_int( package->db, szPreselected, 0 ))
7053 TRACE("Preselected property is set, not migrating feature states\n");
7054 return ERROR_SUCCESS;
7056 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7057 if (r == ERROR_SUCCESS)
7059 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7060 msiobj_release( &view->hdr );
7061 if (r != ERROR_SUCCESS)
7062 return r;
7064 return ERROR_SUCCESS;
7067 static void bind_image( const char *filename, const char *path )
7069 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7071 WARN("failed to bind image %u\n", GetLastError());
7075 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7077 UINT i;
7078 MSIFILE *file;
7079 MSIPACKAGE *package = param;
7080 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7081 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7082 char *filenameA, *pathA;
7083 WCHAR *pathW, **path_list;
7085 if (!(file = msi_get_loaded_file( package, key )))
7087 WARN("file %s not found\n", debugstr_w(key));
7088 return ERROR_SUCCESS;
7090 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7091 path_list = msi_split_string( paths, ';' );
7092 if (!path_list) bind_image( filenameA, NULL );
7093 else
7095 for (i = 0; path_list[i] && path_list[i][0]; i++)
7097 deformat_string( package, path_list[i], &pathW );
7098 if ((pathA = strdupWtoA( pathW )))
7100 bind_image( filenameA, pathA );
7101 msi_free( pathA );
7103 msi_free( pathW );
7106 msi_free( path_list );
7107 msi_free( filenameA );
7108 return ERROR_SUCCESS;
7111 static UINT ACTION_BindImage( MSIPACKAGE *package )
7113 static const WCHAR query[] = {
7114 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7115 'B','i','n','d','I','m','a','g','e',0};
7116 MSIQUERY *view;
7117 UINT r;
7119 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7120 if (r == ERROR_SUCCESS)
7122 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7123 msiobj_release( &view->hdr );
7124 if (r != ERROR_SUCCESS)
7125 return r;
7127 return ERROR_SUCCESS;
7130 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7132 static const WCHAR query[] = {
7133 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7134 MSIQUERY *view;
7135 DWORD count = 0;
7136 UINT r;
7138 r = MSI_OpenQuery( package->db, &view, query, table );
7139 if (r == ERROR_SUCCESS)
7141 r = MSI_IterateRecords(view, &count, NULL, package);
7142 msiobj_release(&view->hdr);
7143 if (r != ERROR_SUCCESS)
7144 return r;
7146 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7147 return ERROR_SUCCESS;
7150 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7152 static const WCHAR table[] = {
7153 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7154 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7157 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7159 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7160 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7163 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7165 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7166 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7169 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7171 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7172 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7175 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7177 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7178 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7181 static const struct
7183 const WCHAR *action;
7184 UINT (*handler)(MSIPACKAGE *);
7185 const WCHAR *action_rollback;
7187 StandardActions[] =
7189 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7190 { szAppSearch, ACTION_AppSearch, NULL },
7191 { szBindImage, ACTION_BindImage, NULL },
7192 { szCCPSearch, ACTION_CCPSearch, NULL },
7193 { szCostFinalize, ACTION_CostFinalize, NULL },
7194 { szCostInitialize, ACTION_CostInitialize, NULL },
7195 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7196 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7197 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7198 { szDisableRollback, ACTION_DisableRollback, NULL },
7199 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7200 { szExecuteAction, ACTION_ExecuteAction, NULL },
7201 { szFileCost, ACTION_FileCost, NULL },
7202 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7203 { szForceReboot, ACTION_ForceReboot, NULL },
7204 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7205 { szInstallExecute, ACTION_InstallExecute, NULL },
7206 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7207 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7208 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7209 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7210 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7211 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7212 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7213 { szInstallValidate, ACTION_InstallValidate, NULL },
7214 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7215 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7216 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7217 { szMoveFiles, ACTION_MoveFiles, NULL },
7218 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7219 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7220 { szPatchFiles, ACTION_PatchFiles, NULL },
7221 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7222 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7223 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7224 { szPublishProduct, ACTION_PublishProduct, NULL },
7225 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7226 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7227 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7228 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7229 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7230 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7231 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7232 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7233 { szRegisterUser, ACTION_RegisterUser, NULL },
7234 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7235 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7236 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7237 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7238 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7239 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7240 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7241 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7242 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7243 { szResolveSource, ACTION_ResolveSource, NULL },
7244 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7245 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7246 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7247 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7248 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7249 { szStartServices, ACTION_StartServices, szStopServices },
7250 { szStopServices, ACTION_StopServices, szStartServices },
7251 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7252 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7253 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7254 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7255 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7256 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7257 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7258 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7259 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7260 { szValidateProductID, ACTION_ValidateProductID, NULL },
7261 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7262 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7263 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7264 { NULL, NULL, NULL }
7267 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7269 BOOL ret = FALSE;
7270 UINT i;
7272 i = 0;
7273 while (StandardActions[i].action != NULL)
7275 if (!strcmpW( StandardActions[i].action, action ))
7277 ui_actionstart( package, action );
7278 if (StandardActions[i].handler)
7280 ui_actioninfo( package, action, TRUE, 0 );
7281 *rc = StandardActions[i].handler( package );
7282 ui_actioninfo( package, action, FALSE, *rc );
7284 if (StandardActions[i].action_rollback && !package->need_rollback)
7286 TRACE("scheduling rollback action\n");
7287 msi_schedule_action( package, ROLLBACK_SCRIPT, StandardActions[i].action_rollback );
7290 else
7292 FIXME("unhandled standard action %s\n", debugstr_w(action));
7293 *rc = ERROR_SUCCESS;
7295 ret = TRUE;
7296 break;
7298 i++;
7300 return ret;
7303 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7305 UINT rc = ERROR_SUCCESS;
7306 BOOL handled;
7308 TRACE("Performing action (%s)\n", debugstr_w(action));
7310 handled = ACTION_HandleStandardAction(package, action, &rc);
7312 if (!handled)
7313 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7315 if (!handled)
7317 WARN("unhandled msi action %s\n", debugstr_w(action));
7318 rc = ERROR_FUNCTION_NOT_CALLED;
7321 return rc;
7324 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7326 UINT rc = ERROR_SUCCESS;
7327 BOOL handled = FALSE;
7329 TRACE("Performing action (%s)\n", debugstr_w(action));
7331 handled = ACTION_HandleStandardAction(package, action, &rc);
7333 if (!handled)
7334 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7336 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7337 handled = TRUE;
7339 if (!handled)
7341 WARN("unhandled msi action %s\n", debugstr_w(action));
7342 rc = ERROR_FUNCTION_NOT_CALLED;
7345 return rc;
7348 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7350 UINT rc = ERROR_SUCCESS;
7351 MSIRECORD *row;
7353 static const WCHAR query[] =
7354 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7355 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7356 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7357 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7358 static const WCHAR ui_query[] =
7359 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7360 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7361 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7362 ' ', '=',' ','%','i',0};
7364 if (needs_ui_sequence(package))
7365 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7366 else
7367 row = MSI_QueryGetRecord(package->db, query, seq);
7369 if (row)
7371 LPCWSTR action, cond;
7373 TRACE("Running the actions\n");
7375 /* check conditions */
7376 cond = MSI_RecordGetString(row, 2);
7378 /* this is a hack to skip errors in the condition code */
7379 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7381 msiobj_release(&row->hdr);
7382 return ERROR_SUCCESS;
7385 action = MSI_RecordGetString(row, 1);
7386 if (!action)
7388 ERR("failed to fetch action\n");
7389 msiobj_release(&row->hdr);
7390 return ERROR_FUNCTION_FAILED;
7393 if (needs_ui_sequence(package))
7394 rc = ACTION_PerformUIAction(package, action, -1);
7395 else
7396 rc = ACTION_PerformAction(package, action, -1);
7398 msiobj_release(&row->hdr);
7401 return rc;
7404 /****************************************************
7405 * TOP level entry points
7406 *****************************************************/
7408 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7409 LPCWSTR szCommandLine )
7411 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7412 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7413 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7414 WCHAR *reinstall = NULL;
7415 BOOL ui_exists;
7416 UINT rc;
7418 msi_set_property( package->db, szAction, szInstall );
7420 package->script->InWhatSequence = SEQUENCE_INSTALL;
7422 if (szPackagePath)
7424 LPWSTR p, dir;
7425 LPCWSTR file;
7427 dir = strdupW(szPackagePath);
7428 p = strrchrW(dir, '\\');
7429 if (p)
7431 *(++p) = 0;
7432 file = szPackagePath + (p - dir);
7434 else
7436 msi_free(dir);
7437 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7438 GetCurrentDirectoryW(MAX_PATH, dir);
7439 lstrcatW(dir, szBackSlash);
7440 file = szPackagePath;
7443 msi_free( package->PackagePath );
7444 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7445 if (!package->PackagePath)
7447 msi_free(dir);
7448 return ERROR_OUTOFMEMORY;
7451 lstrcpyW(package->PackagePath, dir);
7452 lstrcatW(package->PackagePath, file);
7453 msi_free(dir);
7455 msi_set_sourcedir_props(package, FALSE);
7458 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7459 if (rc != ERROR_SUCCESS)
7460 return rc;
7462 msi_apply_transforms( package );
7463 msi_apply_patches( package );
7465 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7467 TRACE("setting reinstall property\n");
7468 msi_set_property( package->db, szReinstall, szAll );
7471 /* properties may have been added by a transform */
7472 msi_clone_properties( package );
7474 msi_parse_command_line( package, szCommandLine, FALSE );
7475 msi_adjust_privilege_properties( package );
7476 msi_set_context( package );
7478 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7480 TRACE("disabling rollback\n");
7481 msi_set_property( package->db, szRollbackDisabled, szOne );
7484 if (needs_ui_sequence( package))
7486 package->script->InWhatSequence |= SEQUENCE_UI;
7487 rc = ACTION_ProcessUISequence(package);
7488 ui_exists = ui_sequence_exists(package);
7489 if (rc == ERROR_SUCCESS || !ui_exists)
7491 package->script->InWhatSequence |= SEQUENCE_EXEC;
7492 rc = ACTION_ProcessExecSequence(package, ui_exists);
7495 else
7496 rc = ACTION_ProcessExecSequence(package, FALSE);
7498 package->script->CurrentlyScripting = FALSE;
7500 /* process the ending type action */
7501 if (rc == ERROR_SUCCESS)
7502 ACTION_PerformActionSequence(package, -1);
7503 else if (rc == ERROR_INSTALL_USEREXIT)
7504 ACTION_PerformActionSequence(package, -2);
7505 else if (rc == ERROR_INSTALL_SUSPEND)
7506 ACTION_PerformActionSequence(package, -4);
7507 else /* failed */
7509 ACTION_PerformActionSequence(package, -3);
7510 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7512 package->need_rollback = TRUE;
7516 /* finish up running custom actions */
7517 ACTION_FinishCustomActions(package);
7519 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7521 WARN("installation failed, running rollback script\n");
7522 execute_script( package, ROLLBACK_SCRIPT );
7524 msi_free( reinstall );
7526 if (rc == ERROR_SUCCESS && package->need_reboot)
7527 return ERROR_SUCCESS_REBOOT_REQUIRED;
7529 return rc;