msi: Don't check feature install level on features specified in arguments.
[wine/wine-gecko.git] / dlls / msi / action.c
blob41f367b740b20dd91ef9a4bcaa61995aaeee0cd2
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 BOOL ignore, in_quotes = FALSE;
215 int count = 0, len = 0;
217 for (p = str; *p; p++)
219 ignore = FALSE;
220 switch (state)
222 case state_whitespace:
223 switch (*p)
225 case ' ':
226 in_quotes = TRUE;
227 ignore = TRUE;
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 in_quotes = TRUE;
238 len++;
239 break;
241 break;
243 case state_token:
244 switch (*p)
246 case '"':
247 state = state_quote;
248 if (in_quotes) count--;
249 else count++;
250 break;
251 case ' ':
252 state = state_whitespace;
253 if (!count) goto done;
254 in_quotes = TRUE;
255 len++;
256 break;
257 default:
258 if (!count) in_quotes = FALSE;
259 else in_quotes = TRUE;
260 len++;
261 break;
263 break;
265 case state_quote:
266 switch (*p)
268 case '"':
269 if (in_quotes && p[1] != '\"') count--;
270 else count++;
271 break;
272 case ' ':
273 state = state_whitespace;
274 if (!count || (count > 1 && !len)) goto done;
275 in_quotes = TRUE;
276 len++;
277 break;
278 default:
279 state = state_token;
280 if (!count) in_quotes = FALSE;
281 else in_quotes = TRUE;
282 len++;
283 break;
285 break;
287 default: break;
289 if (!ignore) *out++ = *p;
292 done:
293 if (!len) *value = 0;
294 else *out = 0;
296 *quotes = count;
297 return p - str;
300 static void remove_quotes( WCHAR *str )
302 WCHAR *p = str;
303 int len = strlenW( str );
305 while ((p = strchrW( p, '"' )))
307 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
308 p++;
312 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
313 BOOL preserve_case )
315 LPCWSTR ptr, ptr2;
316 int num_quotes;
317 DWORD len;
318 WCHAR *prop, *val;
319 UINT r;
321 if (!szCommandLine)
322 return ERROR_SUCCESS;
324 ptr = szCommandLine;
325 while (*ptr)
327 while (*ptr == ' ') ptr++;
328 if (!*ptr) break;
330 ptr2 = strchrW( ptr, '=' );
331 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
333 len = ptr2 - ptr;
334 if (!len) return ERROR_INVALID_COMMAND_LINE;
336 while (ptr[len - 1] == ' ') len--;
338 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
339 memcpy( prop, ptr, len * sizeof(WCHAR) );
340 prop[len] = 0;
341 if (!preserve_case) struprW( prop );
343 ptr2++;
344 while (*ptr2 == ' ') ptr2++;
346 num_quotes = 0;
347 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
348 len = parse_prop( ptr2, val, &num_quotes );
349 if (num_quotes % 2)
351 WARN("unbalanced quotes\n");
352 msi_free( val );
353 msi_free( prop );
354 return ERROR_INVALID_COMMAND_LINE;
356 remove_quotes( val );
357 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
359 r = msi_set_property( package->db, prop, val, -1 );
360 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
361 msi_reset_folders( package, TRUE );
363 msi_free( val );
364 msi_free( prop );
366 ptr = ptr2 + len;
369 return ERROR_SUCCESS;
372 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
374 LPCWSTR pc;
375 LPWSTR p, *ret = NULL;
376 UINT count = 0;
378 if (!str)
379 return ret;
381 /* count the number of substrings */
382 for ( pc = str, count = 0; pc; count++ )
384 pc = strchrW( pc, sep );
385 if (pc)
386 pc++;
389 /* allocate space for an array of substring pointers and the substrings */
390 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
391 (lstrlenW(str)+1) * sizeof(WCHAR) );
392 if (!ret)
393 return ret;
395 /* copy the string and set the pointers */
396 p = (LPWSTR) &ret[count+1];
397 lstrcpyW( p, str );
398 for( count = 0; (ret[count] = p); count++ )
400 p = strchrW( p, sep );
401 if (p)
402 *p++ = 0;
405 return ret;
408 static BOOL ui_sequence_exists( MSIPACKAGE *package )
410 static const WCHAR query [] = {
411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
412 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
413 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
414 MSIQUERY *view;
415 DWORD count = 0;
417 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
419 MSI_IterateRecords( view, &count, NULL, package );
420 msiobj_release( &view->hdr );
422 return count != 0;
425 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
427 WCHAR *source, *check, *p, *db;
428 DWORD len;
430 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
431 return ERROR_OUTOFMEMORY;
433 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
435 msi_free(db);
436 return ERROR_SUCCESS;
438 len = p - db + 2;
439 source = msi_alloc( len * sizeof(WCHAR) );
440 lstrcpynW( source, db, len );
441 msi_free( db );
443 check = msi_dup_property( package->db, szSourceDir );
444 if (!check || replace)
446 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
447 if (r == ERROR_SUCCESS)
448 msi_reset_folders( package, TRUE );
450 msi_free( check );
452 check = msi_dup_property( package->db, szSOURCEDIR );
453 if (!check || replace)
454 msi_set_property( package->db, szSOURCEDIR, source, -1 );
456 msi_free( check );
457 msi_free( source );
459 return ERROR_SUCCESS;
462 static BOOL needs_ui_sequence(MSIPACKAGE *package)
464 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
467 UINT msi_set_context(MSIPACKAGE *package)
469 UINT r = msi_locate_product( package->ProductCode, &package->Context );
470 if (r != ERROR_SUCCESS)
472 int num = msi_get_property_int( package->db, szAllUsers, 0 );
473 if (num == 1 || num == 2)
474 package->Context = MSIINSTALLCONTEXT_MACHINE;
475 else
476 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
478 return ERROR_SUCCESS;
481 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
483 UINT rc;
484 LPCWSTR cond, action;
485 MSIPACKAGE *package = param;
487 action = MSI_RecordGetString(row,1);
488 if (!action)
490 ERR("Error is retrieving action name\n");
491 return ERROR_FUNCTION_FAILED;
494 /* check conditions */
495 cond = MSI_RecordGetString(row,2);
497 /* this is a hack to skip errors in the condition code */
498 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
500 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
501 return ERROR_SUCCESS;
504 if (needs_ui_sequence(package))
505 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
506 else
507 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
509 msi_dialog_check_messages( NULL );
511 if (package->CurrentInstallState != ERROR_SUCCESS)
512 rc = package->CurrentInstallState;
514 if (rc == ERROR_FUNCTION_NOT_CALLED)
515 rc = ERROR_SUCCESS;
517 if (rc != ERROR_SUCCESS)
518 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
520 if (package->need_reboot_now)
522 TRACE("action %s asked for immediate reboot, suspending installation\n",
523 debugstr_w(action));
524 rc = ACTION_ForceReboot( package );
526 return rc;
529 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
531 static const WCHAR query[] = {
532 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
533 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
534 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
535 '`','S','e','q','u','e','n','c','e','`',0};
536 MSIQUERY *view;
537 UINT r;
539 TRACE("%p %s\n", package, debugstr_w(table));
541 r = MSI_OpenQuery( package->db, &view, query, table );
542 if (r == ERROR_SUCCESS)
544 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
545 msiobj_release(&view->hdr);
547 return r;
550 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
552 static const WCHAR query[] = {
553 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
554 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
555 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
556 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
557 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
558 static const WCHAR query_validate[] = {
559 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
560 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
561 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
562 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
563 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
564 MSIQUERY *view;
565 INT seq = 0;
566 UINT rc;
568 if (package->script->ExecuteSequenceRun)
570 TRACE("Execute Sequence already Run\n");
571 return ERROR_SUCCESS;
574 package->script->ExecuteSequenceRun = TRUE;
576 /* get the sequence number */
577 if (UIran)
579 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
580 if (!row) return ERROR_FUNCTION_FAILED;
581 seq = MSI_RecordGetInteger(row,1);
582 msiobj_release(&row->hdr);
584 rc = MSI_OpenQuery(package->db, &view, query, seq);
585 if (rc == ERROR_SUCCESS)
587 TRACE("Running the actions\n");
589 msi_set_property( package->db, szSourceDir, NULL, -1 );
590 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
591 msiobj_release(&view->hdr);
593 return rc;
596 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
598 static const WCHAR query[] = {
599 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
600 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
601 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
602 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
603 MSIQUERY *view;
604 UINT rc;
606 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
607 if (rc == ERROR_SUCCESS)
609 TRACE("Running the actions\n");
610 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611 msiobj_release(&view->hdr);
613 return rc;
616 /********************************************************
617 * ACTION helper functions and functions that perform the actions
618 *******************************************************/
619 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
621 BOOL ret=FALSE;
622 UINT arc;
624 arc = ACTION_CustomAction( package, action, script );
625 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
627 *rc = arc;
628 ret = TRUE;
630 return ret;
633 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
635 MSICOMPONENT *comp;
637 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
639 if (!strcmpW( Component, comp->Component )) return comp;
641 return NULL;
644 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
646 MSIFEATURE *feature;
648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
650 if (!strcmpW( Feature, feature->Feature )) return feature;
652 return NULL;
655 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
657 MSIFILE *file;
659 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
661 if (!strcmpW( key, file->File )) return file;
663 return NULL;
666 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
668 MSIFOLDER *folder;
670 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
672 if (!strcmpW( dir, folder->Directory )) return folder;
674 return NULL;
678 * Recursively create all directories in the path.
679 * shamelessly stolen from setupapi/queue.c
681 BOOL msi_create_full_path( const WCHAR *path )
683 BOOL ret = TRUE;
684 WCHAR *new_path;
685 int len;
687 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
688 strcpyW( new_path, path );
690 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
691 new_path[len - 1] = 0;
693 while (!CreateDirectoryW( new_path, NULL ))
695 WCHAR *slash;
696 DWORD last_error = GetLastError();
697 if (last_error == ERROR_ALREADY_EXISTS) break;
698 if (last_error != ERROR_PATH_NOT_FOUND)
700 ret = FALSE;
701 break;
703 if (!(slash = strrchrW( new_path, '\\' )))
705 ret = FALSE;
706 break;
708 len = slash - new_path;
709 new_path[len] = 0;
710 if (!msi_create_full_path( new_path ))
712 ret = FALSE;
713 break;
715 new_path[len] = '\\';
717 msi_free( new_path );
718 return ret;
721 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
723 MSIRECORD *row;
725 row = MSI_CreateRecord( 4 );
726 MSI_RecordSetInteger( row, 1, a );
727 MSI_RecordSetInteger( row, 2, b );
728 MSI_RecordSetInteger( row, 3, c );
729 MSI_RecordSetInteger( row, 4, d );
730 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
731 msiobj_release( &row->hdr );
733 msi_dialog_check_messages( NULL );
736 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
738 static const WCHAR query[] =
739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
740 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
741 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
742 WCHAR message[1024];
743 MSIRECORD *row = 0;
744 DWORD size;
746 if (!package->LastAction || strcmpW( package->LastAction, action ))
748 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
750 if (MSI_RecordIsNull( row, 3 ))
752 msiobj_release( &row->hdr );
753 return;
755 /* update the cached action format */
756 msi_free( package->ActionFormat );
757 package->ActionFormat = msi_dup_record_field( row, 3 );
758 msi_free( package->LastAction );
759 package->LastAction = strdupW( action );
760 msiobj_release( &row->hdr );
762 size = 1024;
763 MSI_RecordSetStringW( record, 0, package->ActionFormat );
764 MSI_FormatRecordW( package, record, message, &size );
765 row = MSI_CreateRecord( 1 );
766 MSI_RecordSetStringW( row, 1, message );
767 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
768 msiobj_release( &row->hdr );
771 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
773 if (!comp->Enabled)
775 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
776 return INSTALLSTATE_UNKNOWN;
778 if (package->need_rollback) return comp->Installed;
779 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
781 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
782 return INSTALLSTATE_UNKNOWN;
784 return comp->ActionRequest;
787 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
789 if (package->need_rollback) return feature->Installed;
790 return feature->ActionRequest;
793 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
795 MSIPACKAGE *package = param;
796 LPCWSTR dir, component, full_path;
797 MSIRECORD *uirow;
798 MSIFOLDER *folder;
799 MSICOMPONENT *comp;
801 component = MSI_RecordGetString(row, 2);
802 if (!component)
803 return ERROR_SUCCESS;
805 comp = msi_get_loaded_component(package, component);
806 if (!comp)
807 return ERROR_SUCCESS;
809 comp->Action = msi_get_component_action( package, comp );
810 if (comp->Action != INSTALLSTATE_LOCAL)
812 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
813 return ERROR_SUCCESS;
816 dir = MSI_RecordGetString(row,1);
817 if (!dir)
819 ERR("Unable to get folder id\n");
820 return ERROR_SUCCESS;
823 uirow = MSI_CreateRecord(1);
824 MSI_RecordSetStringW(uirow, 1, dir);
825 msi_ui_actiondata(package, szCreateFolders, uirow);
826 msiobj_release(&uirow->hdr);
828 full_path = msi_get_target_folder( package, dir );
829 if (!full_path)
831 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
832 return ERROR_SUCCESS;
834 TRACE("folder is %s\n", debugstr_w(full_path));
836 folder = msi_get_loaded_folder( package, dir );
837 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
838 folder->State = FOLDER_STATE_CREATED;
839 return ERROR_SUCCESS;
842 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
844 static const WCHAR query[] = {
845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
846 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
847 MSIQUERY *view;
848 UINT rc;
850 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
851 if (rc != ERROR_SUCCESS)
852 return ERROR_SUCCESS;
854 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
855 msiobj_release(&view->hdr);
856 return rc;
859 static void remove_persistent_folder( MSIFOLDER *folder )
861 FolderList *fl;
863 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
865 remove_persistent_folder( fl->folder );
867 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
869 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
873 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
875 MSIPACKAGE *package = param;
876 LPCWSTR dir, component, full_path;
877 MSIRECORD *uirow;
878 MSIFOLDER *folder;
879 MSICOMPONENT *comp;
881 component = MSI_RecordGetString(row, 2);
882 if (!component)
883 return ERROR_SUCCESS;
885 comp = msi_get_loaded_component(package, component);
886 if (!comp)
887 return ERROR_SUCCESS;
889 comp->Action = msi_get_component_action( package, comp );
890 if (comp->Action != INSTALLSTATE_ABSENT)
892 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
893 return ERROR_SUCCESS;
896 dir = MSI_RecordGetString( row, 1 );
897 if (!dir)
899 ERR("Unable to get folder id\n");
900 return ERROR_SUCCESS;
903 full_path = msi_get_target_folder( package, dir );
904 if (!full_path)
906 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
907 return ERROR_SUCCESS;
909 TRACE("folder is %s\n", debugstr_w(full_path));
911 uirow = MSI_CreateRecord( 1 );
912 MSI_RecordSetStringW( uirow, 1, dir );
913 msi_ui_actiondata( package, szRemoveFolders, uirow );
914 msiobj_release( &uirow->hdr );
916 folder = msi_get_loaded_folder( package, dir );
917 remove_persistent_folder( folder );
918 return ERROR_SUCCESS;
921 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
923 static const WCHAR query[] = {
924 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
925 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
926 MSIQUERY *view;
927 UINT rc;
929 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
930 if (rc != ERROR_SUCCESS)
931 return ERROR_SUCCESS;
933 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
934 msiobj_release( &view->hdr );
935 return rc;
938 static UINT load_component( MSIRECORD *row, LPVOID param )
940 MSIPACKAGE *package = param;
941 MSICOMPONENT *comp;
943 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
944 if (!comp)
945 return ERROR_FUNCTION_FAILED;
947 list_add_tail( &package->components, &comp->entry );
949 /* fill in the data */
950 comp->Component = msi_dup_record_field( row, 1 );
952 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
954 comp->ComponentId = msi_dup_record_field( row, 2 );
955 comp->Directory = msi_dup_record_field( row, 3 );
956 comp->Attributes = MSI_RecordGetInteger(row,4);
957 comp->Condition = msi_dup_record_field( row, 5 );
958 comp->KeyPath = msi_dup_record_field( row, 6 );
960 comp->Installed = INSTALLSTATE_UNKNOWN;
961 comp->Action = INSTALLSTATE_UNKNOWN;
962 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
964 comp->assembly = msi_load_assembly( package, comp );
965 return ERROR_SUCCESS;
968 UINT msi_load_all_components( MSIPACKAGE *package )
970 static const WCHAR query[] = {
971 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
972 '`','C','o','m','p','o','n','e','n','t','`',0};
973 MSIQUERY *view;
974 UINT r;
976 if (!list_empty(&package->components))
977 return ERROR_SUCCESS;
979 r = MSI_DatabaseOpenViewW( package->db, query, &view );
980 if (r != ERROR_SUCCESS)
981 return r;
983 if (!msi_init_assembly_caches( package ))
985 ERR("can't initialize assembly caches\n");
986 msiobj_release( &view->hdr );
987 return ERROR_FUNCTION_FAILED;
990 r = MSI_IterateRecords(view, NULL, load_component, package);
991 msiobj_release(&view->hdr);
992 return r;
995 typedef struct {
996 MSIPACKAGE *package;
997 MSIFEATURE *feature;
998 } _ilfs;
1000 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1002 ComponentList *cl;
1004 cl = msi_alloc( sizeof (*cl) );
1005 if ( !cl )
1006 return ERROR_NOT_ENOUGH_MEMORY;
1007 cl->component = comp;
1008 list_add_tail( &feature->Components, &cl->entry );
1010 return ERROR_SUCCESS;
1013 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1015 FeatureList *fl;
1017 fl = msi_alloc( sizeof(*fl) );
1018 if ( !fl )
1019 return ERROR_NOT_ENOUGH_MEMORY;
1020 fl->feature = child;
1021 list_add_tail( &parent->Children, &fl->entry );
1023 return ERROR_SUCCESS;
1026 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1028 _ilfs* ilfs = param;
1029 LPCWSTR component;
1030 MSICOMPONENT *comp;
1032 component = MSI_RecordGetString(row,1);
1034 /* check to see if the component is already loaded */
1035 comp = msi_get_loaded_component( ilfs->package, component );
1036 if (!comp)
1038 WARN("ignoring unknown component %s\n", debugstr_w(component));
1039 return ERROR_SUCCESS;
1041 add_feature_component( ilfs->feature, comp );
1042 comp->Enabled = TRUE;
1044 return ERROR_SUCCESS;
1047 static UINT load_feature(MSIRECORD * row, LPVOID param)
1049 static const WCHAR query[] = {
1050 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1051 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1052 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1053 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1054 MSIPACKAGE *package = param;
1055 MSIFEATURE *feature;
1056 MSIQUERY *view;
1057 _ilfs ilfs;
1058 UINT rc;
1060 /* fill in the data */
1062 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1063 if (!feature)
1064 return ERROR_NOT_ENOUGH_MEMORY;
1066 list_init( &feature->Children );
1067 list_init( &feature->Components );
1069 feature->Feature = msi_dup_record_field( row, 1 );
1071 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1073 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1074 feature->Title = msi_dup_record_field( row, 3 );
1075 feature->Description = msi_dup_record_field( row, 4 );
1077 if (!MSI_RecordIsNull(row,5))
1078 feature->Display = MSI_RecordGetInteger(row,5);
1080 feature->Level= MSI_RecordGetInteger(row,6);
1081 feature->Directory = msi_dup_record_field( row, 7 );
1082 feature->Attributes = MSI_RecordGetInteger(row,8);
1084 feature->Installed = INSTALLSTATE_UNKNOWN;
1085 feature->Action = INSTALLSTATE_UNKNOWN;
1086 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1088 list_add_tail( &package->features, &feature->entry );
1090 /* load feature components */
1092 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1093 if (rc != ERROR_SUCCESS)
1094 return ERROR_SUCCESS;
1096 ilfs.package = package;
1097 ilfs.feature = feature;
1099 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1100 msiobj_release(&view->hdr);
1101 return rc;
1104 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1106 MSIPACKAGE *package = param;
1107 MSIFEATURE *parent, *child;
1109 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1110 if (!child)
1111 return ERROR_FUNCTION_FAILED;
1113 if (!child->Feature_Parent)
1114 return ERROR_SUCCESS;
1116 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1117 if (!parent)
1118 return ERROR_FUNCTION_FAILED;
1120 add_feature_child( parent, child );
1121 return ERROR_SUCCESS;
1124 UINT msi_load_all_features( MSIPACKAGE *package )
1126 static const WCHAR query[] = {
1127 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1128 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1129 '`','D','i','s','p','l','a','y','`',0};
1130 MSIQUERY *view;
1131 UINT r;
1133 if (!list_empty(&package->features))
1134 return ERROR_SUCCESS;
1136 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1137 if (r != ERROR_SUCCESS)
1138 return r;
1140 r = MSI_IterateRecords( view, NULL, load_feature, package );
1141 if (r != ERROR_SUCCESS)
1143 msiobj_release( &view->hdr );
1144 return r;
1146 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1147 msiobj_release( &view->hdr );
1148 return r;
1151 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1153 if (!p)
1154 return p;
1155 p = strchrW(p, ch);
1156 if (!p)
1157 return p;
1158 *p = 0;
1159 return p+1;
1162 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1164 static const WCHAR query[] = {
1165 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1166 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1167 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1168 MSIQUERY *view = NULL;
1169 MSIRECORD *row = NULL;
1170 UINT r;
1172 TRACE("%s\n", debugstr_w(file->File));
1174 r = MSI_OpenQuery(package->db, &view, query, file->File);
1175 if (r != ERROR_SUCCESS)
1176 goto done;
1178 r = MSI_ViewExecute(view, NULL);
1179 if (r != ERROR_SUCCESS)
1180 goto done;
1182 r = MSI_ViewFetch(view, &row);
1183 if (r != ERROR_SUCCESS)
1184 goto done;
1186 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1187 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1188 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1189 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1190 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1192 done:
1193 if (view) msiobj_release(&view->hdr);
1194 if (row) msiobj_release(&row->hdr);
1195 return r;
1198 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1200 MSIRECORD *row;
1201 static const WCHAR query[] = {
1202 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1203 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1204 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1206 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1207 if (!row)
1209 WARN("query failed\n");
1210 return ERROR_FUNCTION_FAILED;
1213 file->disk_id = MSI_RecordGetInteger( row, 1 );
1214 msiobj_release( &row->hdr );
1215 return ERROR_SUCCESS;
1218 static UINT load_file(MSIRECORD *row, LPVOID param)
1220 MSIPACKAGE* package = param;
1221 LPCWSTR component;
1222 MSIFILE *file;
1224 /* fill in the data */
1226 file = msi_alloc_zero( sizeof (MSIFILE) );
1227 if (!file)
1228 return ERROR_NOT_ENOUGH_MEMORY;
1230 file->File = msi_dup_record_field( row, 1 );
1232 component = MSI_RecordGetString( row, 2 );
1233 file->Component = msi_get_loaded_component( package, component );
1235 if (!file->Component)
1237 WARN("Component not found: %s\n", debugstr_w(component));
1238 msi_free(file->File);
1239 msi_free(file);
1240 return ERROR_SUCCESS;
1243 file->FileName = msi_dup_record_field( row, 3 );
1244 msi_reduce_to_long_filename( file->FileName );
1246 file->ShortName = msi_dup_record_field( row, 3 );
1247 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1249 file->FileSize = MSI_RecordGetInteger( row, 4 );
1250 file->Version = msi_dup_record_field( row, 5 );
1251 file->Language = msi_dup_record_field( row, 6 );
1252 file->Attributes = MSI_RecordGetInteger( row, 7 );
1253 file->Sequence = MSI_RecordGetInteger( row, 8 );
1255 file->state = msifs_invalid;
1257 /* if the compressed bits are not set in the file attributes,
1258 * then read the information from the package word count property
1260 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1262 file->IsCompressed = FALSE;
1264 else if (file->Attributes &
1265 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1267 file->IsCompressed = TRUE;
1269 else if (file->Attributes & msidbFileAttributesNoncompressed)
1271 file->IsCompressed = FALSE;
1273 else
1275 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1278 load_file_hash(package, file);
1279 load_file_disk_id(package, file);
1281 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1283 list_add_tail( &package->files, &file->entry );
1285 return ERROR_SUCCESS;
1288 static UINT load_all_files(MSIPACKAGE *package)
1290 static const WCHAR query[] = {
1291 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1292 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1293 '`','S','e','q','u','e','n','c','e','`', 0};
1294 MSIQUERY *view;
1295 UINT rc;
1297 if (!list_empty(&package->files))
1298 return ERROR_SUCCESS;
1300 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1301 if (rc != ERROR_SUCCESS)
1302 return ERROR_SUCCESS;
1304 rc = MSI_IterateRecords(view, NULL, load_file, package);
1305 msiobj_release(&view->hdr);
1306 return rc;
1309 static UINT load_media( MSIRECORD *row, LPVOID param )
1311 MSIPACKAGE *package = param;
1312 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1313 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1315 /* FIXME: load external cabinets and directory sources too */
1316 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1317 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1318 return ERROR_SUCCESS;
1321 static UINT load_all_media( MSIPACKAGE *package )
1323 static const WCHAR query[] = {
1324 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1325 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1326 '`','D','i','s','k','I','d','`',0};
1327 MSIQUERY *view;
1328 UINT r;
1330 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1331 if (r != ERROR_SUCCESS)
1332 return ERROR_SUCCESS;
1334 r = MSI_IterateRecords( view, NULL, load_media, package );
1335 msiobj_release( &view->hdr );
1336 return r;
1339 static UINT load_patch(MSIRECORD *row, LPVOID param)
1341 MSIPACKAGE *package = param;
1342 MSIFILEPATCH *patch;
1343 LPWSTR file_key;
1345 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1346 if (!patch)
1347 return ERROR_NOT_ENOUGH_MEMORY;
1349 file_key = msi_dup_record_field( row, 1 );
1350 patch->File = msi_get_loaded_file( package, file_key );
1351 msi_free(file_key);
1353 if( !patch->File )
1355 ERR("Failed to find target for patch in File table\n");
1356 msi_free(patch);
1357 return ERROR_FUNCTION_FAILED;
1360 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1362 /* FIXME: The database should be properly transformed */
1363 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1365 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1366 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1367 patch->IsApplied = FALSE;
1369 /* FIXME:
1370 * Header field - for patch validation.
1371 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1374 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1376 list_add_tail( &package->filepatches, &patch->entry );
1378 return ERROR_SUCCESS;
1381 static UINT load_all_patches(MSIPACKAGE *package)
1383 static const WCHAR query[] = {
1384 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1385 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1386 '`','S','e','q','u','e','n','c','e','`',0};
1387 MSIQUERY *view;
1388 UINT rc;
1390 if (!list_empty(&package->filepatches))
1391 return ERROR_SUCCESS;
1393 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1394 if (rc != ERROR_SUCCESS)
1395 return ERROR_SUCCESS;
1397 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1398 msiobj_release(&view->hdr);
1399 return rc;
1402 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1404 static const WCHAR query[] = {
1405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1406 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1407 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1408 MSIQUERY *view;
1410 folder->persistent = FALSE;
1411 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1413 if (!MSI_ViewExecute( view, NULL ))
1415 MSIRECORD *rec;
1416 if (!MSI_ViewFetch( view, &rec ))
1418 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1419 folder->persistent = TRUE;
1420 msiobj_release( &rec->hdr );
1423 msiobj_release( &view->hdr );
1425 return ERROR_SUCCESS;
1428 static UINT load_folder( MSIRECORD *row, LPVOID param )
1430 MSIPACKAGE *package = param;
1431 static WCHAR szEmpty[] = { 0 };
1432 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1433 MSIFOLDER *folder;
1435 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1436 list_init( &folder->children );
1437 folder->Directory = msi_dup_record_field( row, 1 );
1438 folder->Parent = msi_dup_record_field( row, 2 );
1439 p = msi_dup_record_field(row, 3);
1441 TRACE("%s\n", debugstr_w(folder->Directory));
1443 /* split src and target dir */
1444 tgt_short = p;
1445 src_short = folder_split_path( p, ':' );
1447 /* split the long and short paths */
1448 tgt_long = folder_split_path( tgt_short, '|' );
1449 src_long = folder_split_path( src_short, '|' );
1451 /* check for no-op dirs */
1452 if (tgt_short && !strcmpW( szDot, tgt_short ))
1453 tgt_short = szEmpty;
1454 if (src_short && !strcmpW( szDot, src_short ))
1455 src_short = szEmpty;
1457 if (!tgt_long)
1458 tgt_long = tgt_short;
1460 if (!src_short) {
1461 src_short = tgt_short;
1462 src_long = tgt_long;
1465 if (!src_long)
1466 src_long = src_short;
1468 /* FIXME: use the target short path too */
1469 folder->TargetDefault = strdupW(tgt_long);
1470 folder->SourceShortPath = strdupW(src_short);
1471 folder->SourceLongPath = strdupW(src_long);
1472 msi_free(p);
1474 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1475 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1476 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1478 load_folder_persistence( package, folder );
1480 list_add_tail( &package->folders, &folder->entry );
1481 return ERROR_SUCCESS;
1484 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1486 FolderList *fl;
1488 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1489 fl->folder = child;
1490 list_add_tail( &parent->children, &fl->entry );
1491 return ERROR_SUCCESS;
1494 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1496 MSIPACKAGE *package = param;
1497 MSIFOLDER *parent, *child;
1499 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1500 return ERROR_FUNCTION_FAILED;
1502 if (!child->Parent) return ERROR_SUCCESS;
1504 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1505 return ERROR_FUNCTION_FAILED;
1507 return add_folder_child( parent, child );
1510 static UINT load_all_folders( MSIPACKAGE *package )
1512 static const WCHAR query[] = {
1513 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1514 '`','D','i','r','e','c','t','o','r','y','`',0};
1515 MSIQUERY *view;
1516 UINT r;
1518 if (!list_empty(&package->folders))
1519 return ERROR_SUCCESS;
1521 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1522 if (r != ERROR_SUCCESS)
1523 return r;
1525 r = MSI_IterateRecords( view, NULL, load_folder, package );
1526 if (r != ERROR_SUCCESS)
1528 msiobj_release( &view->hdr );
1529 return r;
1531 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1532 msiobj_release( &view->hdr );
1533 return r;
1536 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1538 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1539 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1541 load_all_folders( package );
1542 msi_load_all_components( package );
1543 msi_load_all_features( package );
1544 load_all_files( package );
1545 load_all_patches( package );
1546 load_all_media( package );
1548 return ERROR_SUCCESS;
1551 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1553 const WCHAR *action = package->script->Actions[script][index];
1554 ui_actionstart( package, action );
1555 TRACE("executing %s\n", debugstr_w(action));
1556 return ACTION_PerformAction( package, action, script );
1559 static UINT execute_script( MSIPACKAGE *package, UINT script )
1561 UINT i, rc = ERROR_SUCCESS;
1563 TRACE("executing script %u\n", script);
1565 if (!package->script)
1567 ERR("no script!\n");
1568 return ERROR_FUNCTION_FAILED;
1570 if (script == SCRIPT_ROLLBACK)
1572 for (i = package->script->ActionCount[script]; i > 0; i--)
1574 rc = execute_script_action( package, script, i - 1 );
1575 if (rc != ERROR_SUCCESS) break;
1578 else
1580 for (i = 0; i < package->script->ActionCount[script]; i++)
1582 rc = execute_script_action( package, script, i );
1583 if (rc != ERROR_SUCCESS) break;
1586 msi_free_action_script(package, script);
1587 return rc;
1590 static UINT ACTION_FileCost(MSIPACKAGE *package)
1592 return ERROR_SUCCESS;
1595 static void get_client_counts( MSIPACKAGE *package )
1597 MSICOMPONENT *comp;
1598 HKEY hkey;
1600 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1602 if (!comp->ComponentId) continue;
1604 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1605 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1607 comp->num_clients = 0;
1608 continue;
1610 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1611 NULL, NULL, NULL, NULL );
1612 RegCloseKey( hkey );
1616 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1618 MSICOMPONENT *comp;
1619 UINT r;
1621 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1623 if (!comp->ComponentId) continue;
1625 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1626 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1627 &comp->Installed );
1628 if (r == ERROR_SUCCESS) continue;
1630 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1631 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1632 &comp->Installed );
1633 if (r == ERROR_SUCCESS) continue;
1635 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1636 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1637 &comp->Installed );
1638 if (r == ERROR_SUCCESS) continue;
1640 comp->Installed = INSTALLSTATE_ABSENT;
1644 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1646 MSIFEATURE *feature;
1648 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1650 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1652 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1653 feature->Installed = INSTALLSTATE_ABSENT;
1654 else
1655 feature->Installed = state;
1659 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1661 return (feature->Level > 0 && feature->Level <= level);
1664 static BOOL process_state_property(MSIPACKAGE* package, int level,
1665 LPCWSTR property, INSTALLSTATE state)
1667 LPWSTR override;
1668 MSIFEATURE *feature;
1669 BOOL remove = !strcmpW(property, szRemove);
1670 BOOL reinstall = !strcmpW(property, szReinstall);
1672 override = msi_dup_property( package->db, property );
1673 if (!override)
1674 return FALSE;
1676 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1678 if (feature->Level <= 0)
1679 continue;
1681 if (reinstall)
1682 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1683 else if (remove)
1684 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1686 if (!strcmpiW( override, szAll ))
1688 feature->Action = state;
1689 feature->ActionRequest = state;
1691 else
1693 LPWSTR ptr = override;
1694 LPWSTR ptr2 = strchrW(override,',');
1696 while (ptr)
1698 int len = ptr2 - ptr;
1700 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1701 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1705 break;
1707 if (ptr2)
1709 ptr=ptr2+1;
1710 ptr2 = strchrW(ptr,',');
1712 else
1713 break;
1717 msi_free(override);
1718 return TRUE;
1721 static BOOL process_overrides( MSIPACKAGE *package, int level )
1723 static const WCHAR szAddLocal[] =
1724 {'A','D','D','L','O','C','A','L',0};
1725 static const WCHAR szAddSource[] =
1726 {'A','D','D','S','O','U','R','C','E',0};
1727 static const WCHAR szAdvertise[] =
1728 {'A','D','V','E','R','T','I','S','E',0};
1729 BOOL ret = FALSE;
1731 /* all these activation/deactivation things happen in order and things
1732 * later on the list override things earlier on the list.
1734 * 0 INSTALLLEVEL processing
1735 * 1 ADDLOCAL
1736 * 2 REMOVE
1737 * 3 ADDSOURCE
1738 * 4 ADDDEFAULT
1739 * 5 REINSTALL
1740 * 6 ADVERTISE
1741 * 7 COMPADDLOCAL
1742 * 8 COMPADDSOURCE
1743 * 9 FILEADDLOCAL
1744 * 10 FILEADDSOURCE
1745 * 11 FILEADDDEFAULT
1747 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1748 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1749 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1750 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1751 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1753 if (ret && !package->full_reinstall)
1754 msi_set_property( package->db, szPreselected, szOne, -1 );
1756 return ret;
1759 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1761 int level;
1762 MSICOMPONENT* component;
1763 MSIFEATURE *feature;
1765 TRACE("Checking Install Level\n");
1767 level = msi_get_property_int(package->db, szInstallLevel, 1);
1769 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1771 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1773 if (!is_feature_selected( feature, level )) continue;
1775 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1777 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1779 feature->Action = INSTALLSTATE_SOURCE;
1780 feature->ActionRequest = INSTALLSTATE_SOURCE;
1782 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1784 feature->Action = INSTALLSTATE_ADVERTISED;
1785 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1787 else
1789 feature->Action = INSTALLSTATE_LOCAL;
1790 feature->ActionRequest = INSTALLSTATE_LOCAL;
1794 /* disable child features of unselected parent or follow parent */
1795 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1797 FeatureList *fl;
1799 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1801 if (!is_feature_selected( feature, level ))
1803 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1804 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1806 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1808 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1809 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1810 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1811 fl->feature->Action = feature->Action;
1812 fl->feature->ActionRequest = feature->ActionRequest;
1817 else /* preselected */
1819 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1821 if (!is_feature_selected( feature, level )) continue;
1823 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1825 if (feature->Installed == INSTALLSTATE_ABSENT)
1827 feature->Action = INSTALLSTATE_UNKNOWN;
1828 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1830 else
1832 feature->Action = feature->Installed;
1833 feature->ActionRequest = feature->Installed;
1837 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1839 FeatureList *fl;
1841 if (!is_feature_selected( feature, level )) continue;
1843 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1845 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1846 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1848 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1849 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1850 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1851 fl->feature->Action = feature->Action;
1852 fl->feature->ActionRequest = feature->ActionRequest;
1858 /* now we want to set component state based based on feature state */
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 ComponentList *cl;
1863 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1864 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1865 feature->ActionRequest, feature->Action);
1867 /* features with components that have compressed files are made local */
1868 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1870 if (cl->component->ForceLocalState &&
1871 feature->ActionRequest == INSTALLSTATE_SOURCE)
1873 feature->Action = INSTALLSTATE_LOCAL;
1874 feature->ActionRequest = INSTALLSTATE_LOCAL;
1875 break;
1879 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1881 component = cl->component;
1883 switch (feature->ActionRequest)
1885 case INSTALLSTATE_ABSENT:
1886 component->anyAbsent = 1;
1887 break;
1888 case INSTALLSTATE_ADVERTISED:
1889 component->hasAdvertisedFeature = 1;
1890 break;
1891 case INSTALLSTATE_SOURCE:
1892 component->hasSourceFeature = 1;
1893 break;
1894 case INSTALLSTATE_LOCAL:
1895 component->hasLocalFeature = 1;
1896 break;
1897 case INSTALLSTATE_DEFAULT:
1898 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1899 component->hasAdvertisedFeature = 1;
1900 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1901 component->hasSourceFeature = 1;
1902 else
1903 component->hasLocalFeature = 1;
1904 break;
1905 default:
1906 break;
1911 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1913 /* check if it's local or source */
1914 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1915 (component->hasLocalFeature || component->hasSourceFeature))
1917 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1918 !component->ForceLocalState)
1920 component->Action = INSTALLSTATE_SOURCE;
1921 component->ActionRequest = INSTALLSTATE_SOURCE;
1923 else
1925 component->Action = INSTALLSTATE_LOCAL;
1926 component->ActionRequest = INSTALLSTATE_LOCAL;
1928 continue;
1931 /* if any feature is local, the component must be local too */
1932 if (component->hasLocalFeature)
1934 component->Action = INSTALLSTATE_LOCAL;
1935 component->ActionRequest = INSTALLSTATE_LOCAL;
1936 continue;
1938 if (component->hasSourceFeature)
1940 component->Action = INSTALLSTATE_SOURCE;
1941 component->ActionRequest = INSTALLSTATE_SOURCE;
1942 continue;
1944 if (component->hasAdvertisedFeature)
1946 component->Action = INSTALLSTATE_ADVERTISED;
1947 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1948 continue;
1950 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1951 if (component->anyAbsent && component->ComponentId)
1953 component->Action = INSTALLSTATE_ABSENT;
1954 component->ActionRequest = INSTALLSTATE_ABSENT;
1958 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1960 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1962 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1963 component->Action = INSTALLSTATE_LOCAL;
1964 component->ActionRequest = INSTALLSTATE_LOCAL;
1967 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1968 component->Installed == INSTALLSTATE_SOURCE &&
1969 component->hasSourceFeature)
1971 component->Action = INSTALLSTATE_UNKNOWN;
1972 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1975 TRACE("component %s (installed %d request %d action %d)\n",
1976 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1978 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1979 component->num_clients++;
1980 else if (component->Action == INSTALLSTATE_ABSENT)
1981 component->num_clients--;
1984 return ERROR_SUCCESS;
1987 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1989 MSIPACKAGE *package = param;
1990 LPCWSTR name;
1991 MSIFEATURE *feature;
1993 name = MSI_RecordGetString( row, 1 );
1995 feature = msi_get_loaded_feature( package, name );
1996 if (!feature)
1997 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1998 else
2000 LPCWSTR Condition;
2001 Condition = MSI_RecordGetString(row,3);
2003 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2005 int level = MSI_RecordGetInteger(row,2);
2006 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2007 feature->Level = level;
2010 return ERROR_SUCCESS;
2013 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2015 static const WCHAR name[] = {'\\',0};
2016 VS_FIXEDFILEINFO *ptr, *ret;
2017 LPVOID version;
2018 DWORD versize, handle;
2019 UINT sz;
2021 versize = GetFileVersionInfoSizeW( filename, &handle );
2022 if (!versize)
2023 return NULL;
2025 version = msi_alloc( versize );
2026 if (!version)
2027 return NULL;
2029 GetFileVersionInfoW( filename, 0, versize, version );
2031 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2033 msi_free( version );
2034 return NULL;
2037 ret = msi_alloc( sz );
2038 memcpy( ret, ptr, sz );
2040 msi_free( version );
2041 return ret;
2044 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2046 DWORD ms, ls;
2048 msi_parse_version_string( version, &ms, &ls );
2050 if (fi->dwFileVersionMS > ms) return 1;
2051 else if (fi->dwFileVersionMS < ms) return -1;
2052 else if (fi->dwFileVersionLS > ls) return 1;
2053 else if (fi->dwFileVersionLS < ls) return -1;
2054 return 0;
2057 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2059 DWORD ms1, ms2;
2061 msi_parse_version_string( ver1, &ms1, NULL );
2062 msi_parse_version_string( ver2, &ms2, NULL );
2064 if (ms1 > ms2) return 1;
2065 else if (ms1 < ms2) return -1;
2066 return 0;
2069 DWORD msi_get_disk_file_size( LPCWSTR filename )
2071 HANDLE file;
2072 DWORD size;
2074 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2075 if (file == INVALID_HANDLE_VALUE)
2076 return INVALID_FILE_SIZE;
2078 size = GetFileSize( file, NULL );
2079 TRACE("size is %u\n", size);
2080 CloseHandle( file );
2081 return size;
2084 BOOL msi_file_hash_matches( MSIFILE *file )
2086 UINT r;
2087 MSIFILEHASHINFO hash;
2089 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2090 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2091 if (r != ERROR_SUCCESS)
2092 return FALSE;
2094 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2097 static WCHAR *get_temp_dir( void )
2099 static UINT id;
2100 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2102 GetTempPathW( MAX_PATH, tmp );
2103 for (;;)
2105 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2106 if (CreateDirectoryW( dir, NULL )) break;
2108 return strdupW( dir );
2112 * msi_build_directory_name()
2114 * This function is to save messing round with directory names
2115 * It handles adding backslashes between path segments,
2116 * and can add \ at the end of the directory name if told to.
2118 * It takes a variable number of arguments.
2119 * It always allocates a new string for the result, so make sure
2120 * to free the return value when finished with it.
2122 * The first arg is the number of path segments that follow.
2123 * The arguments following count are a list of path segments.
2124 * A path segment may be NULL.
2126 * Path segments will be added with a \ separating them.
2127 * A \ will not be added after the last segment, however if the
2128 * last segment is NULL, then the last character will be a \
2130 WCHAR *msi_build_directory_name( DWORD count, ... )
2132 DWORD sz = 1, i;
2133 WCHAR *dir;
2134 va_list va;
2136 va_start( va, count );
2137 for (i = 0; i < count; i++)
2139 const WCHAR *str = va_arg( va, const WCHAR * );
2140 if (str) sz += strlenW( str ) + 1;
2142 va_end( va );
2144 dir = msi_alloc( sz * sizeof(WCHAR) );
2145 dir[0] = 0;
2147 va_start( va, count );
2148 for (i = 0; i < count; i++)
2150 const WCHAR *str = va_arg( va, const WCHAR * );
2151 if (!str) continue;
2152 strcatW( dir, str );
2153 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2155 va_end( va );
2156 return dir;
2159 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2161 MSIASSEMBLY *assembly = file->Component->assembly;
2163 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2165 msi_free( file->TargetPath );
2166 if (assembly && !assembly->application)
2168 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2169 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2170 msi_track_tempfile( package, file->TargetPath );
2172 else
2174 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2175 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2178 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2181 static UINT calculate_file_cost( MSIPACKAGE *package )
2183 VS_FIXEDFILEINFO *file_version;
2184 WCHAR *font_version;
2185 MSIFILE *file;
2187 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2189 MSICOMPONENT *comp = file->Component;
2190 DWORD file_size;
2192 if (!comp->Enabled) continue;
2194 if (file->IsCompressed)
2195 comp->ForceLocalState = TRUE;
2197 set_target_path( package, file );
2199 if ((comp->assembly && !comp->assembly->installed) ||
2200 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2202 comp->Cost += file->FileSize;
2203 continue;
2205 file_size = msi_get_disk_file_size( file->TargetPath );
2207 if (file->Version)
2209 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2211 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2213 comp->Cost += file->FileSize - file_size;
2215 msi_free( file_version );
2216 continue;
2218 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2220 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2222 comp->Cost += file->FileSize - file_size;
2224 msi_free( font_version );
2225 continue;
2228 if (file_size != file->FileSize)
2230 comp->Cost += file->FileSize - file_size;
2233 return ERROR_SUCCESS;
2236 WCHAR *msi_normalize_path( const WCHAR *in )
2238 const WCHAR *p = in;
2239 WCHAR *q, *ret;
2240 int n, len = strlenW( in ) + 2;
2242 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2244 len = 0;
2245 while (1)
2247 /* copy until the end of the string or a space */
2248 while (*p != ' ' && (*q = *p))
2250 p++, len++;
2251 /* reduce many backslashes to one */
2252 if (*p != '\\' || *q != '\\')
2253 q++;
2256 /* quit at the end of the string */
2257 if (!*p)
2258 break;
2260 /* count the number of spaces */
2261 n = 0;
2262 while (p[n] == ' ')
2263 n++;
2265 /* if it's leading or trailing space, skip it */
2266 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2267 p += n;
2268 else /* copy n spaces */
2269 while (n && (*q++ = *p++)) n--;
2271 while (q - ret > 0 && q[-1] == ' ') q--;
2272 if (q - ret > 0 && q[-1] != '\\')
2274 q[0] = '\\';
2275 q[1] = 0;
2277 return ret;
2280 static WCHAR *get_install_location( MSIPACKAGE *package )
2282 HKEY hkey;
2283 WCHAR *path;
2285 if (!package->ProductCode) return NULL;
2286 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2287 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2289 msi_free( path );
2290 path = NULL;
2292 RegCloseKey( hkey );
2293 return path;
2296 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2298 FolderList *fl;
2299 MSIFOLDER *folder, *parent, *child;
2300 WCHAR *path, *normalized_path;
2302 TRACE("resolving %s\n", debugstr_w(name));
2304 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2306 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2308 if (!(path = get_install_location( package )) &&
2309 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2311 path = msi_dup_property( package->db, szRootDrive );
2314 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2316 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2318 parent = msi_get_loaded_folder( package, folder->Parent );
2319 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2321 else
2322 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2324 normalized_path = msi_normalize_path( path );
2325 msi_free( path );
2326 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2328 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2329 msi_free( normalized_path );
2330 return;
2332 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2333 msi_free( folder->ResolvedTarget );
2334 folder->ResolvedTarget = normalized_path;
2336 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2338 child = fl->folder;
2339 msi_resolve_target_folder( package, child->Directory, load_prop );
2341 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2344 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2346 static const WCHAR query[] =
2347 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2348 '`','C','o','n','d','i','t','i','o','n','`',0};
2349 static const WCHAR szOutOfDiskSpace[] =
2350 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2351 static const WCHAR szPrimaryFolder[] =
2352 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2353 static const WCHAR szPrimaryVolumePath[] =
2354 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2355 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2356 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2357 'A','v','a','i','l','a','b','l','e',0};
2358 MSICOMPONENT *comp;
2359 MSIQUERY *view;
2360 WCHAR *level, *primary_key, *primary_folder;
2361 UINT rc;
2363 TRACE("Building directory properties\n");
2364 msi_resolve_target_folder( package, szTargetDir, TRUE );
2366 TRACE("Evaluating component conditions\n");
2367 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2369 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2371 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2372 comp->Enabled = FALSE;
2374 else
2375 comp->Enabled = TRUE;
2377 get_client_counts( package );
2379 /* read components states from the registry */
2380 ACTION_GetComponentInstallStates(package);
2381 ACTION_GetFeatureInstallStates(package);
2383 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2385 TRACE("Evaluating feature conditions\n");
2387 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2388 if (rc == ERROR_SUCCESS)
2390 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2391 msiobj_release( &view->hdr );
2392 if (rc != ERROR_SUCCESS)
2393 return rc;
2397 TRACE("Calculating file cost\n");
2398 calculate_file_cost( package );
2400 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2401 /* set default run level if not set */
2402 level = msi_dup_property( package->db, szInstallLevel );
2403 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2404 msi_free(level);
2406 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2408 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2410 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2411 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2413 ULARGE_INTEGER free;
2415 primary_folder[2] = 0;
2416 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2418 static const WCHAR fmtW[] = {'%','l','u',0};
2419 WCHAR buf[21];
2421 sprintfW( buf, fmtW, free.QuadPart / 512 );
2422 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2424 toupperW( primary_folder[0] );
2425 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2427 msi_free( primary_folder );
2429 msi_free( primary_key );
2432 /* FIXME: check volume disk space */
2433 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2435 return MSI_SetFeatureStates(package);
2438 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size )
2440 BYTE *data = NULL;
2442 if (!value)
2444 *size = sizeof(WCHAR);
2445 *type = REG_SZ;
2446 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2447 return data;
2449 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2451 if (value[1]=='x')
2453 LPWSTR ptr;
2454 CHAR byte[5];
2455 LPWSTR deformated = NULL;
2456 int count;
2458 deformat_string(package, &value[2], &deformated);
2460 /* binary value type */
2461 ptr = deformated;
2462 *type = REG_BINARY;
2463 if (strlenW(ptr)%2)
2464 *size = (strlenW(ptr)/2)+1;
2465 else
2466 *size = strlenW(ptr)/2;
2468 data = msi_alloc(*size);
2470 byte[0] = '0';
2471 byte[1] = 'x';
2472 byte[4] = 0;
2473 count = 0;
2474 /* if uneven pad with a zero in front */
2475 if (strlenW(ptr)%2)
2477 byte[2]= '0';
2478 byte[3]= *ptr;
2479 ptr++;
2480 data[count] = (BYTE)strtol(byte,NULL,0);
2481 count ++;
2482 TRACE("Uneven byte count\n");
2484 while (*ptr)
2486 byte[2]= *ptr;
2487 ptr++;
2488 byte[3]= *ptr;
2489 ptr++;
2490 data[count] = (BYTE)strtol(byte,NULL,0);
2491 count ++;
2493 msi_free(deformated);
2495 TRACE("Data %i bytes(%i)\n",*size,count);
2497 else
2499 LPWSTR deformated;
2500 LPWSTR p;
2501 DWORD d = 0;
2502 deformat_string(package, &value[1], &deformated);
2504 *type=REG_DWORD;
2505 *size = sizeof(DWORD);
2506 data = msi_alloc(*size);
2507 p = deformated;
2508 if (*p == '-')
2509 p++;
2510 while (*p)
2512 if ( (*p < '0') || (*p > '9') )
2513 break;
2514 d *= 10;
2515 d += (*p - '0');
2516 p++;
2518 if (deformated[0] == '-')
2519 d = -d;
2520 *(LPDWORD)data = d;
2521 TRACE("DWORD %i\n",*(LPDWORD)data);
2523 msi_free(deformated);
2526 else
2528 const WCHAR *ptr = value;
2529 DWORD len;
2531 *type = REG_SZ;
2532 if (value[0] == '#')
2534 ptr++;
2535 if (value[1] == '%')
2537 ptr++;
2538 *type = REG_EXPAND_SZ;
2541 len = deformat_string( package, ptr, (WCHAR **)&data );
2542 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2543 *size = (len + 1) * sizeof(WCHAR);
2545 return data;
2548 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2550 const WCHAR *ret;
2552 switch (root)
2554 case -1:
2555 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2557 *root_key = HKEY_LOCAL_MACHINE;
2558 ret = szHLM;
2560 else
2562 *root_key = HKEY_CURRENT_USER;
2563 ret = szHCU;
2565 break;
2566 case 0:
2567 *root_key = HKEY_CLASSES_ROOT;
2568 ret = szHCR;
2569 break;
2570 case 1:
2571 *root_key = HKEY_CURRENT_USER;
2572 ret = szHCU;
2573 break;
2574 case 2:
2575 *root_key = HKEY_LOCAL_MACHINE;
2576 ret = szHLM;
2577 break;
2578 case 3:
2579 *root_key = HKEY_USERS;
2580 ret = szHU;
2581 break;
2582 default:
2583 ERR("Unknown root %i\n", root);
2584 return NULL;
2587 return ret;
2590 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2592 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2593 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2595 if ((is_64bit || is_wow64) &&
2596 !(comp->Attributes & msidbComponentAttributes64bit) &&
2597 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2599 UINT size;
2600 WCHAR *path_32node;
2602 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2603 if (!(path_32node = msi_alloc( size ))) return NULL;
2605 memcpy( path_32node, path, len * sizeof(WCHAR) );
2606 strcpyW( path_32node + len, szWow6432Node );
2607 strcatW( path_32node, szBackSlash );
2608 strcatW( path_32node, path + len );
2609 return path_32node;
2611 return strdupW( path );
2614 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2616 REGSAM access = KEY_ALL_ACCESS;
2617 WCHAR *subkey, *p, *q;
2618 HKEY hkey, ret = NULL;
2619 LONG res;
2621 if (is_wow64) access |= KEY_WOW64_64KEY;
2623 if (!(subkey = strdupW( path ))) return NULL;
2624 p = subkey;
2625 if ((q = strchrW( p, '\\' ))) *q = 0;
2626 if (create)
2627 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2628 else
2629 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2630 if (res)
2632 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2633 msi_free( subkey );
2634 return NULL;
2636 if (q && q[1])
2638 ret = open_key( hkey, q + 1, create );
2639 RegCloseKey( hkey );
2641 else ret = hkey;
2642 msi_free( subkey );
2643 return ret;
2646 static BOOL is_special_entry( const WCHAR *name )
2648 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2651 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2653 const WCHAR *p = str;
2654 WCHAR **ret;
2655 int i = 0;
2657 *count = 0;
2658 if (!str) return NULL;
2659 while ((p - str) < len)
2661 p += strlenW( p ) + 1;
2662 (*count)++;
2664 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2665 p = str;
2666 while ((p - str) < len)
2668 if (!(ret[i] = strdupW( p )))
2670 for (; i >= 0; i--) msi_free( ret[i] );
2671 msi_free( ret );
2672 return NULL;
2674 p += strlenW( p ) + 1;
2675 i++;
2677 return ret;
2680 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2681 WCHAR **right, DWORD right_count, DWORD *size )
2683 WCHAR *ret, *p;
2684 unsigned int i;
2686 *size = sizeof(WCHAR);
2687 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2688 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2690 if (!(ret = p = msi_alloc( *size ))) return NULL;
2692 for (i = 0; i < left_count; i++)
2694 strcpyW( p, left[i] );
2695 p += strlenW( p ) + 1;
2697 for (i = 0; i < right_count; i++)
2699 strcpyW( p, right[i] );
2700 p += strlenW( p ) + 1;
2702 *p = 0;
2703 return ret;
2706 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2707 WCHAR **new, DWORD new_count )
2709 DWORD ret = old_count;
2710 unsigned int i, j, k;
2712 for (i = 0; i < new_count; i++)
2714 for (j = 0; j < old_count; j++)
2716 if (old[j] && !strcmpW( new[i], old[j] ))
2718 msi_free( old[j] );
2719 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2720 old[k] = NULL;
2721 ret--;
2725 return ret;
2728 enum join_op
2730 JOIN_OP_APPEND,
2731 JOIN_OP_PREPEND,
2732 JOIN_OP_REPLACE
2735 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2736 WCHAR **new, DWORD new_count, DWORD *size )
2738 switch (op)
2740 case JOIN_OP_APPEND:
2741 old_count = remove_duplicate_values( old, old_count, new, new_count );
2742 return flatten_multi_string_values( old, old_count, new, new_count, size );
2744 case JOIN_OP_PREPEND:
2745 old_count = remove_duplicate_values( old, old_count, new, new_count );
2746 return flatten_multi_string_values( new, new_count, old, old_count, size );
2748 case JOIN_OP_REPLACE:
2749 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2751 default:
2752 ERR("unhandled join op %u\n", op);
2753 return NULL;
2757 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2758 BYTE *new_value, DWORD new_size, DWORD *size )
2760 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2761 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2762 enum join_op op = JOIN_OP_REPLACE;
2763 WCHAR **old = NULL, **new = NULL;
2764 BYTE *ret;
2766 if (new_size / sizeof(WCHAR) - 1 > 1)
2768 new_ptr = (const WCHAR *)new_value;
2769 new_len = new_size / sizeof(WCHAR) - 1;
2771 if (!new_ptr[0] && new_ptr[new_len - 1])
2773 op = JOIN_OP_APPEND;
2774 new_len--;
2775 new_ptr++;
2777 else if (new_ptr[0] && !new_ptr[new_len - 1])
2779 op = JOIN_OP_PREPEND;
2780 new_len--;
2782 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2784 op = JOIN_OP_REPLACE;
2785 new_len -= 2;
2786 new_ptr++;
2788 new = split_multi_string_values( new_ptr, new_len, &new_count );
2790 if (old_size / sizeof(WCHAR) - 1 > 1)
2792 old_ptr = (const WCHAR *)old_value;
2793 old_len = old_size / sizeof(WCHAR) - 1;
2794 old = split_multi_string_values( old_ptr, old_len, &old_count );
2796 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2797 for (i = 0; i < old_count; i++) msi_free( old[i] );
2798 for (i = 0; i < new_count; i++) msi_free( new[i] );
2799 msi_free( old );
2800 msi_free( new );
2801 return ret;
2804 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2806 BYTE *ret;
2807 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2808 if (!(ret = msi_alloc( *size ))) return NULL;
2809 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2810 return ret;
2813 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2815 MSIPACKAGE *package = param;
2816 BYTE *new_value, *old_value = NULL;
2817 HKEY root_key, hkey;
2818 DWORD type, old_type, new_size, old_size = 0;
2819 LPWSTR deformated, uikey, keypath;
2820 const WCHAR *szRoot, *component, *name, *key, *str;
2821 MSICOMPONENT *comp;
2822 MSIRECORD * uirow;
2823 INT root;
2824 BOOL check_first = FALSE;
2825 int len;
2827 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2829 component = MSI_RecordGetString(row, 6);
2830 comp = msi_get_loaded_component(package,component);
2831 if (!comp)
2832 return ERROR_SUCCESS;
2834 comp->Action = msi_get_component_action( package, comp );
2835 if (comp->Action != INSTALLSTATE_LOCAL)
2837 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2838 return ERROR_SUCCESS;
2841 name = MSI_RecordGetString(row, 4);
2842 if( MSI_RecordIsNull(row,5) && name )
2844 /* null values can have special meanings */
2845 if (name[0]=='-' && name[1] == 0)
2846 return ERROR_SUCCESS;
2847 if ((name[0] == '+' || name[0] == '*') && !name[1])
2848 check_first = TRUE;
2851 root = MSI_RecordGetInteger(row,2);
2852 key = MSI_RecordGetString(row, 3);
2854 szRoot = get_root_key( package, root, &root_key );
2855 if (!szRoot)
2856 return ERROR_SUCCESS;
2858 deformat_string(package, key , &deformated);
2859 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2860 strcpyW(uikey,szRoot);
2861 strcatW(uikey,deformated);
2863 keypath = get_keypath( comp, root_key, deformated );
2864 msi_free( deformated );
2865 if (!(hkey = open_key( root_key, keypath, TRUE )))
2867 ERR("Could not create key %s\n", debugstr_w(keypath));
2868 msi_free(uikey);
2869 msi_free(keypath);
2870 return ERROR_FUNCTION_FAILED;
2872 str = msi_record_get_string( row, 5, &len );
2873 if (str && len > strlenW( str ))
2875 type = REG_MULTI_SZ;
2876 new_size = (len + 1) * sizeof(WCHAR);
2877 new_value = (BYTE *)msi_strdupW( str, len );
2879 else new_value = parse_value( package, str, &type, &new_size );
2880 deformat_string(package, name, &deformated);
2882 if (!is_special_entry( name ))
2884 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2885 if (type == REG_MULTI_SZ)
2887 BYTE *new;
2888 if (old_value && old_type != REG_MULTI_SZ)
2890 msi_free( old_value );
2891 old_value = NULL;
2892 old_size = 0;
2894 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2895 msi_free( new_value );
2896 new_value = new;
2898 if (!check_first)
2900 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2901 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2903 else if (!old_value)
2905 if (deformated || new_size)
2907 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2908 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2911 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2913 RegCloseKey(hkey);
2915 uirow = MSI_CreateRecord(3);
2916 MSI_RecordSetStringW(uirow,2,deformated);
2917 MSI_RecordSetStringW(uirow,1,uikey);
2918 if (type == REG_SZ || type == REG_EXPAND_SZ)
2919 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2920 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2921 msiobj_release( &uirow->hdr );
2923 msi_free(new_value);
2924 msi_free(old_value);
2925 msi_free(deformated);
2926 msi_free(uikey);
2927 msi_free(keypath);
2929 return ERROR_SUCCESS;
2932 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2934 static const WCHAR query[] = {
2935 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2936 '`','R','e','g','i','s','t','r','y','`',0};
2937 MSIQUERY *view;
2938 UINT rc;
2940 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2941 if (rc != ERROR_SUCCESS)
2942 return ERROR_SUCCESS;
2944 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2945 msiobj_release(&view->hdr);
2946 return rc;
2949 static void delete_key( HKEY root, const WCHAR *path )
2951 REGSAM access = 0;
2952 WCHAR *subkey, *p;
2953 HKEY hkey;
2954 LONG res;
2956 if (is_wow64) access |= KEY_WOW64_64KEY;
2958 if (!(subkey = strdupW( path ))) return;
2959 for (;;)
2961 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2962 hkey = open_key( root, subkey, FALSE );
2963 if (!hkey) break;
2964 if (p && p[1])
2965 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2966 else
2967 res = RegDeleteKeyExW( root, subkey, access, 0 );
2968 if (res)
2970 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2971 break;
2973 if (p && p[1]) RegCloseKey( hkey );
2974 else break;
2976 msi_free( subkey );
2979 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2981 LONG res;
2982 HKEY hkey;
2983 DWORD num_subkeys, num_values;
2985 if ((hkey = open_key( root, path, FALSE )))
2987 if ((res = RegDeleteValueW( hkey, value )))
2988 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2990 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2991 NULL, NULL, NULL, NULL );
2992 RegCloseKey( hkey );
2993 if (!res && !num_subkeys && !num_values)
2995 TRACE("removing empty key %s\n", debugstr_w(path));
2996 delete_key( root, path );
3001 static void delete_tree( HKEY root, const WCHAR *path )
3003 LONG res;
3004 HKEY hkey;
3006 if (!(hkey = open_key( root, path, FALSE ))) return;
3007 res = RegDeleteTreeW( hkey, NULL );
3008 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3009 delete_key( root, path );
3010 RegCloseKey( hkey );
3013 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3015 MSIPACKAGE *package = param;
3016 LPCWSTR component, name, key_str, root_key_str;
3017 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3018 MSICOMPONENT *comp;
3019 MSIRECORD *uirow;
3020 BOOL delete_key = FALSE;
3021 HKEY hkey_root;
3022 UINT size;
3023 INT root;
3025 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3027 component = MSI_RecordGetString( row, 6 );
3028 comp = msi_get_loaded_component( package, component );
3029 if (!comp)
3030 return ERROR_SUCCESS;
3032 comp->Action = msi_get_component_action( package, comp );
3033 if (comp->Action != INSTALLSTATE_ABSENT)
3035 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3036 return ERROR_SUCCESS;
3039 name = MSI_RecordGetString( row, 4 );
3040 if (MSI_RecordIsNull( row, 5 ) && name )
3042 if (name[0] == '+' && !name[1])
3043 return ERROR_SUCCESS;
3044 if ((name[0] == '-' || name[0] == '*') && !name[1])
3046 delete_key = TRUE;
3047 name = NULL;
3051 root = MSI_RecordGetInteger( row, 2 );
3052 key_str = MSI_RecordGetString( row, 3 );
3054 root_key_str = get_root_key( package, root, &hkey_root );
3055 if (!root_key_str)
3056 return ERROR_SUCCESS;
3058 deformat_string( package, key_str, &deformated_key );
3059 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3060 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3061 strcpyW( ui_key_str, root_key_str );
3062 strcatW( ui_key_str, deformated_key );
3064 deformat_string( package, name, &deformated_name );
3066 keypath = get_keypath( comp, hkey_root, deformated_key );
3067 msi_free( deformated_key );
3068 if (delete_key) delete_tree( hkey_root, keypath );
3069 else delete_value( hkey_root, keypath, deformated_name );
3070 msi_free( keypath );
3072 uirow = MSI_CreateRecord( 2 );
3073 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3074 MSI_RecordSetStringW( uirow, 2, deformated_name );
3075 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3076 msiobj_release( &uirow->hdr );
3078 msi_free( ui_key_str );
3079 msi_free( deformated_name );
3080 return ERROR_SUCCESS;
3083 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3085 MSIPACKAGE *package = param;
3086 LPCWSTR component, name, key_str, root_key_str;
3087 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3088 MSICOMPONENT *comp;
3089 MSIRECORD *uirow;
3090 BOOL delete_key = FALSE;
3091 HKEY hkey_root;
3092 UINT size;
3093 INT root;
3095 component = MSI_RecordGetString( row, 5 );
3096 comp = msi_get_loaded_component( package, component );
3097 if (!comp)
3098 return ERROR_SUCCESS;
3100 comp->Action = msi_get_component_action( package, comp );
3101 if (comp->Action != INSTALLSTATE_LOCAL)
3103 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3104 return ERROR_SUCCESS;
3107 if ((name = MSI_RecordGetString( row, 4 )))
3109 if (name[0] == '-' && !name[1])
3111 delete_key = TRUE;
3112 name = NULL;
3116 root = MSI_RecordGetInteger( row, 2 );
3117 key_str = MSI_RecordGetString( row, 3 );
3119 root_key_str = get_root_key( package, root, &hkey_root );
3120 if (!root_key_str)
3121 return ERROR_SUCCESS;
3123 deformat_string( package, key_str, &deformated_key );
3124 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3125 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3126 strcpyW( ui_key_str, root_key_str );
3127 strcatW( ui_key_str, deformated_key );
3129 deformat_string( package, name, &deformated_name );
3131 keypath = get_keypath( comp, hkey_root, deformated_key );
3132 msi_free( deformated_key );
3133 if (delete_key) delete_tree( hkey_root, keypath );
3134 else delete_value( hkey_root, keypath, deformated_name );
3135 msi_free( keypath );
3137 uirow = MSI_CreateRecord( 2 );
3138 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3139 MSI_RecordSetStringW( uirow, 2, deformated_name );
3140 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3141 msiobj_release( &uirow->hdr );
3143 msi_free( ui_key_str );
3144 msi_free( deformated_name );
3145 return ERROR_SUCCESS;
3148 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3150 static const WCHAR registry_query[] = {
3151 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3152 '`','R','e','g','i','s','t','r','y','`',0};
3153 static const WCHAR remove_registry_query[] = {
3154 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3155 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3156 MSIQUERY *view;
3157 UINT rc;
3159 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3160 if (rc == ERROR_SUCCESS)
3162 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3163 msiobj_release( &view->hdr );
3164 if (rc != ERROR_SUCCESS)
3165 return rc;
3167 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3168 if (rc == ERROR_SUCCESS)
3170 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3171 msiobj_release( &view->hdr );
3172 if (rc != ERROR_SUCCESS)
3173 return rc;
3175 return ERROR_SUCCESS;
3178 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3180 return ERROR_SUCCESS;
3184 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3186 static const WCHAR query[]= {
3187 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3188 '`','R','e','g','i','s','t','r','y','`',0};
3189 MSICOMPONENT *comp;
3190 DWORD total = 0, count = 0;
3191 MSIQUERY *view;
3192 MSIFEATURE *feature;
3193 MSIFILE *file;
3194 UINT rc;
3196 TRACE("InstallValidate\n");
3198 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3199 if (rc == ERROR_SUCCESS)
3201 rc = MSI_IterateRecords( view, &count, NULL, package );
3202 msiobj_release( &view->hdr );
3203 if (rc != ERROR_SUCCESS)
3204 return rc;
3205 total += count * REG_PROGRESS_VALUE;
3207 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3208 total += COMPONENT_PROGRESS_VALUE;
3210 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3211 total += file->FileSize;
3213 msi_ui_progress( package, 0, total, 0, 0 );
3215 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3217 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3218 debugstr_w(feature->Feature), feature->Installed,
3219 feature->ActionRequest, feature->Action);
3221 return ERROR_SUCCESS;
3224 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3226 MSIPACKAGE* package = param;
3227 LPCWSTR cond = NULL;
3228 LPCWSTR message = NULL;
3229 UINT r;
3231 static const WCHAR title[]=
3232 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3234 cond = MSI_RecordGetString(row,1);
3236 r = MSI_EvaluateConditionW(package,cond);
3237 if (r == MSICONDITION_FALSE)
3239 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3241 LPWSTR deformated;
3242 message = MSI_RecordGetString(row,2);
3243 deformat_string(package,message,&deformated);
3244 MessageBoxW(NULL,deformated,title,MB_OK);
3245 msi_free(deformated);
3248 return ERROR_INSTALL_FAILURE;
3251 return ERROR_SUCCESS;
3254 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3256 static const WCHAR query[] = {
3257 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3258 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3259 MSIQUERY *view;
3260 UINT rc;
3262 TRACE("Checking launch conditions\n");
3264 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3265 if (rc != ERROR_SUCCESS)
3266 return ERROR_SUCCESS;
3268 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3269 msiobj_release(&view->hdr);
3270 return rc;
3273 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3276 if (!cmp->KeyPath)
3277 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3279 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3281 static const WCHAR query[] = {
3282 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3283 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3284 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3285 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3286 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3287 MSIRECORD *row;
3288 UINT root, len;
3289 LPWSTR deformated, buffer, deformated_name;
3290 LPCWSTR key, name;
3292 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3293 if (!row)
3294 return NULL;
3296 root = MSI_RecordGetInteger(row,2);
3297 key = MSI_RecordGetString(row, 3);
3298 name = MSI_RecordGetString(row, 4);
3299 deformat_string(package, key , &deformated);
3300 deformat_string(package, name, &deformated_name);
3302 len = strlenW(deformated) + 6;
3303 if (deformated_name)
3304 len+=strlenW(deformated_name);
3306 buffer = msi_alloc( len *sizeof(WCHAR));
3308 if (deformated_name)
3309 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3310 else
3311 sprintfW(buffer,fmt,root,deformated);
3313 msi_free(deformated);
3314 msi_free(deformated_name);
3315 msiobj_release(&row->hdr);
3317 return buffer;
3319 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3321 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3322 return NULL;
3324 else
3326 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3328 if (file)
3329 return strdupW( file->TargetPath );
3331 return NULL;
3334 static HKEY openSharedDLLsKey(void)
3336 HKEY hkey=0;
3337 static const WCHAR path[] =
3338 {'S','o','f','t','w','a','r','e','\\',
3339 'M','i','c','r','o','s','o','f','t','\\',
3340 'W','i','n','d','o','w','s','\\',
3341 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3342 'S','h','a','r','e','d','D','L','L','s',0};
3344 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3345 return hkey;
3348 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3350 HKEY hkey;
3351 DWORD count=0;
3352 DWORD type;
3353 DWORD sz = sizeof(count);
3354 DWORD rc;
3356 hkey = openSharedDLLsKey();
3357 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3358 if (rc != ERROR_SUCCESS)
3359 count = 0;
3360 RegCloseKey(hkey);
3361 return count;
3364 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3366 HKEY hkey;
3368 hkey = openSharedDLLsKey();
3369 if (count > 0)
3370 msi_reg_set_val_dword( hkey, path, count );
3371 else
3372 RegDeleteValueW(hkey,path);
3373 RegCloseKey(hkey);
3374 return count;
3377 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3379 MSIFEATURE *feature;
3380 INT count = 0;
3381 BOOL write = FALSE;
3383 /* only refcount DLLs */
3384 if (comp->KeyPath == NULL ||
3385 comp->assembly ||
3386 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3387 comp->Attributes & msidbComponentAttributesODBCDataSource)
3388 write = FALSE;
3389 else
3391 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3392 write = (count > 0);
3394 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3395 write = TRUE;
3398 /* increment counts */
3399 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3401 ComponentList *cl;
3403 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3404 continue;
3406 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3408 if ( cl->component == comp )
3409 count++;
3413 /* decrement counts */
3414 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3416 ComponentList *cl;
3418 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3419 continue;
3421 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3423 if ( cl->component == comp )
3424 count--;
3428 /* ref count all the files in the component */
3429 if (write)
3431 MSIFILE *file;
3433 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3435 if (file->Component == comp)
3436 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3440 /* add a count for permanent */
3441 if (comp->Attributes & msidbComponentAttributesPermanent)
3442 count ++;
3444 comp->RefCount = count;
3446 if (write)
3447 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3450 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3452 if (comp->assembly)
3454 const WCHAR prefixW[] = {'<','\\',0};
3455 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3456 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3458 if (keypath)
3460 strcpyW( keypath, prefixW );
3461 strcatW( keypath, comp->assembly->display_name );
3463 return keypath;
3465 return resolve_keypath( package, comp );
3468 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3470 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3471 UINT rc;
3472 MSICOMPONENT *comp;
3473 HKEY hkey;
3475 TRACE("\n");
3477 squash_guid(package->ProductCode,squished_pc);
3478 msi_set_sourcedir_props(package, FALSE);
3480 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3482 MSIRECORD *uirow;
3483 INSTALLSTATE action;
3485 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3486 if (!comp->ComponentId)
3487 continue;
3489 squash_guid( comp->ComponentId, squished_cc );
3490 msi_free( comp->FullKeypath );
3491 comp->FullKeypath = build_full_keypath( package, comp );
3493 ACTION_RefCountComponent( package, comp );
3495 if (package->need_rollback) action = comp->Installed;
3496 else action = comp->ActionRequest;
3498 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3499 debugstr_w(comp->Component), debugstr_w(squished_cc),
3500 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3502 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3504 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3505 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3506 else
3507 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3509 if (rc != ERROR_SUCCESS)
3510 continue;
3512 if (comp->Attributes & msidbComponentAttributesPermanent)
3514 static const WCHAR szPermKey[] =
3515 { '0','0','0','0','0','0','0','0','0','0','0','0',
3516 '0','0','0','0','0','0','0','0','0','0','0','0',
3517 '0','0','0','0','0','0','0','0',0 };
3519 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3521 if (action == INSTALLSTATE_LOCAL)
3522 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3523 else
3525 MSIFILE *file;
3526 MSIRECORD *row;
3527 LPWSTR ptr, ptr2;
3528 WCHAR source[MAX_PATH];
3529 WCHAR base[MAX_PATH];
3530 LPWSTR sourcepath;
3532 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3533 static const WCHAR query[] = {
3534 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3535 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3536 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3537 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3538 '`','D','i','s','k','I','d','`',0};
3540 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3541 continue;
3543 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3544 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3545 ptr2 = strrchrW(source, '\\') + 1;
3546 msiobj_release(&row->hdr);
3548 lstrcpyW(base, package->PackagePath);
3549 ptr = strrchrW(base, '\\');
3550 *(ptr + 1) = '\0';
3552 sourcepath = msi_resolve_file_source(package, file);
3553 ptr = sourcepath + lstrlenW(base);
3554 lstrcpyW(ptr2, ptr);
3555 msi_free(sourcepath);
3557 msi_reg_set_val_str(hkey, squished_pc, source);
3559 RegCloseKey(hkey);
3561 else if (action == INSTALLSTATE_ABSENT)
3563 if (comp->num_clients <= 0)
3565 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3566 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3567 else
3568 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3570 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3572 else
3574 LONG res;
3576 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3577 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3578 else
3579 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3581 if (rc != ERROR_SUCCESS)
3583 WARN( "failed to open component key %u\n", rc );
3584 continue;
3586 res = RegDeleteValueW( hkey, squished_pc );
3587 RegCloseKey(hkey);
3588 if (res) WARN( "failed to delete component value %d\n", res );
3592 /* UI stuff */
3593 uirow = MSI_CreateRecord(3);
3594 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3595 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3596 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3597 msi_ui_actiondata( package, szProcessComponents, uirow );
3598 msiobj_release( &uirow->hdr );
3600 return ERROR_SUCCESS;
3603 typedef struct {
3604 CLSID clsid;
3605 LPWSTR source;
3607 LPWSTR path;
3608 ITypeLib *ptLib;
3609 } typelib_struct;
3611 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3612 LPWSTR lpszName, LONG_PTR lParam)
3614 TLIBATTR *attr;
3615 typelib_struct *tl_struct = (typelib_struct*) lParam;
3616 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3617 int sz;
3618 HRESULT res;
3620 if (!IS_INTRESOURCE(lpszName))
3622 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3623 return TRUE;
3626 sz = strlenW(tl_struct->source)+4;
3627 sz *= sizeof(WCHAR);
3629 if ((INT_PTR)lpszName == 1)
3630 tl_struct->path = strdupW(tl_struct->source);
3631 else
3633 tl_struct->path = msi_alloc(sz);
3634 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3637 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3638 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3639 if (FAILED(res))
3641 msi_free(tl_struct->path);
3642 tl_struct->path = NULL;
3644 return TRUE;
3647 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3648 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3650 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3651 return FALSE;
3654 msi_free(tl_struct->path);
3655 tl_struct->path = NULL;
3657 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3658 ITypeLib_Release(tl_struct->ptLib);
3660 return TRUE;
3663 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3665 MSIPACKAGE* package = param;
3666 LPCWSTR component;
3667 MSICOMPONENT *comp;
3668 MSIFILE *file;
3669 typelib_struct tl_struct;
3670 ITypeLib *tlib;
3671 HMODULE module;
3672 HRESULT hr;
3674 component = MSI_RecordGetString(row,3);
3675 comp = msi_get_loaded_component(package,component);
3676 if (!comp)
3677 return ERROR_SUCCESS;
3679 comp->Action = msi_get_component_action( package, comp );
3680 if (comp->Action != INSTALLSTATE_LOCAL)
3682 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3683 return ERROR_SUCCESS;
3686 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3688 TRACE("component has no key path\n");
3689 return ERROR_SUCCESS;
3691 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3693 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3694 if (module)
3696 LPCWSTR guid;
3697 guid = MSI_RecordGetString(row,1);
3698 CLSIDFromString( guid, &tl_struct.clsid);
3699 tl_struct.source = strdupW( file->TargetPath );
3700 tl_struct.path = NULL;
3702 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3703 (LONG_PTR)&tl_struct);
3705 if (tl_struct.path)
3707 LPCWSTR helpid, help_path = NULL;
3708 HRESULT res;
3710 helpid = MSI_RecordGetString(row,6);
3712 if (helpid) help_path = msi_get_target_folder( package, helpid );
3713 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3715 if (FAILED(res))
3716 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3717 else
3718 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3720 ITypeLib_Release(tl_struct.ptLib);
3721 msi_free(tl_struct.path);
3723 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3725 FreeLibrary(module);
3726 msi_free(tl_struct.source);
3728 else
3730 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3731 if (FAILED(hr))
3733 ERR("Failed to load type library: %08x\n", hr);
3734 return ERROR_INSTALL_FAILURE;
3737 ITypeLib_Release(tlib);
3740 return ERROR_SUCCESS;
3743 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3745 static const WCHAR query[] = {
3746 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3747 '`','T','y','p','e','L','i','b','`',0};
3748 MSIQUERY *view;
3749 UINT rc;
3751 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3752 if (rc != ERROR_SUCCESS)
3753 return ERROR_SUCCESS;
3755 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3756 msiobj_release(&view->hdr);
3757 return rc;
3760 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3762 MSIPACKAGE *package = param;
3763 LPCWSTR component, guid;
3764 MSICOMPONENT *comp;
3765 GUID libid;
3766 UINT version;
3767 LCID language;
3768 SYSKIND syskind;
3769 HRESULT hr;
3771 component = MSI_RecordGetString( row, 3 );
3772 comp = msi_get_loaded_component( package, component );
3773 if (!comp)
3774 return ERROR_SUCCESS;
3776 comp->Action = msi_get_component_action( package, comp );
3777 if (comp->Action != INSTALLSTATE_ABSENT)
3779 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3780 return ERROR_SUCCESS;
3782 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3784 guid = MSI_RecordGetString( row, 1 );
3785 CLSIDFromString( guid, &libid );
3786 version = MSI_RecordGetInteger( row, 4 );
3787 language = MSI_RecordGetInteger( row, 2 );
3789 #ifdef _WIN64
3790 syskind = SYS_WIN64;
3791 #else
3792 syskind = SYS_WIN32;
3793 #endif
3795 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3796 if (FAILED(hr))
3798 WARN("Failed to unregister typelib: %08x\n", hr);
3801 return ERROR_SUCCESS;
3804 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3806 static const WCHAR query[] = {
3807 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3808 '`','T','y','p','e','L','i','b','`',0};
3809 MSIQUERY *view;
3810 UINT rc;
3812 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3813 if (rc != ERROR_SUCCESS)
3814 return ERROR_SUCCESS;
3816 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3817 msiobj_release( &view->hdr );
3818 return rc;
3821 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3823 static const WCHAR szlnk[] = {'.','l','n','k',0};
3824 LPCWSTR directory, extension, link_folder;
3825 LPWSTR link_file, filename;
3827 directory = MSI_RecordGetString( row, 2 );
3828 link_folder = msi_get_target_folder( package, directory );
3829 if (!link_folder)
3831 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3832 return NULL;
3834 /* may be needed because of a bug somewhere else */
3835 msi_create_full_path( link_folder );
3837 filename = msi_dup_record_field( row, 3 );
3838 msi_reduce_to_long_filename( filename );
3840 extension = strrchrW( filename, '.' );
3841 if (!extension || strcmpiW( extension, szlnk ))
3843 int len = strlenW( filename );
3844 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3845 memcpy( filename + len, szlnk, sizeof(szlnk) );
3847 link_file = msi_build_directory_name( 2, link_folder, filename );
3848 msi_free( filename );
3850 return link_file;
3853 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3855 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3856 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3857 WCHAR *folder, *dest, *path;
3859 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3860 folder = msi_dup_property( package->db, szWindowsFolder );
3861 else
3863 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3864 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3865 msi_free( appdata );
3867 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3868 msi_create_full_path( dest );
3869 path = msi_build_directory_name( 2, dest, icon_name );
3870 msi_free( folder );
3871 msi_free( dest );
3872 return path;
3875 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3877 MSIPACKAGE *package = param;
3878 LPWSTR link_file, deformated, path;
3879 LPCWSTR component, target;
3880 MSICOMPONENT *comp;
3881 IShellLinkW *sl = NULL;
3882 IPersistFile *pf = NULL;
3883 HRESULT res;
3885 component = MSI_RecordGetString(row, 4);
3886 comp = msi_get_loaded_component(package, component);
3887 if (!comp)
3888 return ERROR_SUCCESS;
3890 comp->Action = msi_get_component_action( package, comp );
3891 if (comp->Action != INSTALLSTATE_LOCAL)
3893 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3894 return ERROR_SUCCESS;
3896 msi_ui_actiondata( package, szCreateShortcuts, row );
3898 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3899 &IID_IShellLinkW, (LPVOID *) &sl );
3901 if (FAILED( res ))
3903 ERR("CLSID_ShellLink not available\n");
3904 goto err;
3907 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3908 if (FAILED( res ))
3910 ERR("QueryInterface(IID_IPersistFile) failed\n");
3911 goto err;
3914 target = MSI_RecordGetString(row, 5);
3915 if (strchrW(target, '['))
3917 deformat_string( package, target, &path );
3918 TRACE("target path is %s\n", debugstr_w(path));
3919 IShellLinkW_SetPath( sl, path );
3920 msi_free( path );
3922 else
3924 FIXME("poorly handled shortcut format, advertised shortcut\n");
3925 IShellLinkW_SetPath(sl,comp->FullKeypath);
3928 if (!MSI_RecordIsNull(row,6))
3930 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3931 deformat_string(package, arguments, &deformated);
3932 IShellLinkW_SetArguments(sl,deformated);
3933 msi_free(deformated);
3936 if (!MSI_RecordIsNull(row,7))
3938 LPCWSTR description = MSI_RecordGetString(row, 7);
3939 IShellLinkW_SetDescription(sl, description);
3942 if (!MSI_RecordIsNull(row,8))
3943 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3945 if (!MSI_RecordIsNull(row,9))
3947 INT index;
3948 LPCWSTR icon = MSI_RecordGetString(row, 9);
3950 path = msi_build_icon_path(package, icon);
3951 index = MSI_RecordGetInteger(row,10);
3953 /* no value means 0 */
3954 if (index == MSI_NULL_INTEGER)
3955 index = 0;
3957 IShellLinkW_SetIconLocation(sl, path, index);
3958 msi_free(path);
3961 if (!MSI_RecordIsNull(row,11))
3962 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3964 if (!MSI_RecordIsNull(row,12))
3966 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3967 full_path = msi_get_target_folder( package, wkdir );
3968 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3970 link_file = get_link_file(package, row);
3972 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3973 IPersistFile_Save(pf, link_file, FALSE);
3974 msi_free(link_file);
3976 err:
3977 if (pf)
3978 IPersistFile_Release( pf );
3979 if (sl)
3980 IShellLinkW_Release( sl );
3982 return ERROR_SUCCESS;
3985 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3987 static const WCHAR query[] = {
3988 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3989 '`','S','h','o','r','t','c','u','t','`',0};
3990 MSIQUERY *view;
3991 HRESULT res;
3992 UINT rc;
3994 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3995 if (rc != ERROR_SUCCESS)
3996 return ERROR_SUCCESS;
3998 res = CoInitialize( NULL );
4000 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4001 msiobj_release(&view->hdr);
4003 if (SUCCEEDED(res)) CoUninitialize();
4004 return rc;
4007 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4009 MSIPACKAGE *package = param;
4010 LPWSTR link_file;
4011 LPCWSTR component;
4012 MSICOMPONENT *comp;
4014 component = MSI_RecordGetString( row, 4 );
4015 comp = msi_get_loaded_component( package, component );
4016 if (!comp)
4017 return ERROR_SUCCESS;
4019 comp->Action = msi_get_component_action( package, comp );
4020 if (comp->Action != INSTALLSTATE_ABSENT)
4022 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4023 return ERROR_SUCCESS;
4025 msi_ui_actiondata( package, szRemoveShortcuts, row );
4027 link_file = get_link_file( package, row );
4029 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4030 if (!DeleteFileW( link_file ))
4032 WARN("Failed to remove shortcut file %u\n", GetLastError());
4034 msi_free( link_file );
4036 return ERROR_SUCCESS;
4039 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4041 static const WCHAR query[] = {
4042 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4043 '`','S','h','o','r','t','c','u','t','`',0};
4044 MSIQUERY *view;
4045 UINT rc;
4047 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4048 if (rc != ERROR_SUCCESS)
4049 return ERROR_SUCCESS;
4051 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4052 msiobj_release( &view->hdr );
4053 return rc;
4056 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4058 MSIPACKAGE* package = param;
4059 HANDLE the_file;
4060 LPWSTR FilePath;
4061 LPCWSTR FileName;
4062 CHAR buffer[1024];
4063 DWORD sz;
4064 UINT rc;
4066 FileName = MSI_RecordGetString(row,1);
4067 if (!FileName)
4069 ERR("Unable to get FileName\n");
4070 return ERROR_SUCCESS;
4073 FilePath = msi_build_icon_path(package, FileName);
4075 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4077 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4078 FILE_ATTRIBUTE_NORMAL, NULL);
4080 if (the_file == INVALID_HANDLE_VALUE)
4082 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4083 msi_free(FilePath);
4084 return ERROR_SUCCESS;
4089 DWORD write;
4090 sz = 1024;
4091 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4092 if (rc != ERROR_SUCCESS)
4094 ERR("Failed to get stream\n");
4095 CloseHandle(the_file);
4096 DeleteFileW(FilePath);
4097 break;
4099 WriteFile(the_file,buffer,sz,&write,NULL);
4100 } while (sz == 1024);
4102 msi_free(FilePath);
4103 CloseHandle(the_file);
4105 return ERROR_SUCCESS;
4108 static UINT msi_publish_icons(MSIPACKAGE *package)
4110 static const WCHAR query[]= {
4111 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4112 '`','I','c','o','n','`',0};
4113 MSIQUERY *view;
4114 UINT r;
4116 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4117 if (r == ERROR_SUCCESS)
4119 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4120 msiobj_release(&view->hdr);
4121 if (r != ERROR_SUCCESS)
4122 return r;
4124 return ERROR_SUCCESS;
4127 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4129 UINT r;
4130 HKEY source;
4131 LPWSTR buffer;
4132 MSIMEDIADISK *disk;
4133 MSISOURCELISTINFO *info;
4135 r = RegCreateKeyW(hkey, szSourceList, &source);
4136 if (r != ERROR_SUCCESS)
4137 return r;
4139 RegCloseKey(source);
4141 buffer = strrchrW(package->PackagePath, '\\') + 1;
4142 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4143 package->Context, MSICODE_PRODUCT,
4144 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4145 if (r != ERROR_SUCCESS)
4146 return r;
4148 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4149 package->Context, MSICODE_PRODUCT,
4150 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4151 if (r != ERROR_SUCCESS)
4152 return r;
4154 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4155 package->Context, MSICODE_PRODUCT,
4156 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4157 if (r != ERROR_SUCCESS)
4158 return r;
4160 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4162 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4163 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4164 info->options, info->value);
4165 else
4166 MsiSourceListSetInfoW(package->ProductCode, NULL,
4167 info->context, info->options,
4168 info->property, info->value);
4171 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4173 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4174 disk->context, disk->options,
4175 disk->disk_id, disk->volume_label, disk->disk_prompt);
4178 return ERROR_SUCCESS;
4181 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4183 MSIHANDLE hdb, suminfo;
4184 WCHAR guids[MAX_PATH];
4185 WCHAR packcode[SQUISH_GUID_SIZE];
4186 LPWSTR buffer;
4187 LPWSTR ptr;
4188 DWORD langid;
4189 DWORD size;
4190 UINT r;
4192 static const WCHAR szARPProductIcon[] =
4193 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4194 static const WCHAR szAssignment[] =
4195 {'A','s','s','i','g','n','m','e','n','t',0};
4196 static const WCHAR szAdvertiseFlags[] =
4197 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4198 static const WCHAR szClients[] =
4199 {'C','l','i','e','n','t','s',0};
4200 static const WCHAR szColon[] = {':',0};
4202 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4203 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4204 msi_free(buffer);
4206 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4207 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4209 /* FIXME */
4210 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4212 buffer = msi_dup_property(package->db, szARPProductIcon);
4213 if (buffer)
4215 LPWSTR path = msi_build_icon_path(package, buffer);
4216 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4217 msi_free(path);
4218 msi_free(buffer);
4221 buffer = msi_dup_property(package->db, szProductVersion);
4222 if (buffer)
4224 DWORD verdword = msi_version_str_to_dword(buffer);
4225 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4226 msi_free(buffer);
4229 msi_reg_set_val_dword(hkey, szAssignment, 0);
4230 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4231 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4232 msi_reg_set_val_str(hkey, szClients, szColon);
4234 hdb = alloc_msihandle(&package->db->hdr);
4235 if (!hdb)
4236 return ERROR_NOT_ENOUGH_MEMORY;
4238 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4239 MsiCloseHandle(hdb);
4240 if (r != ERROR_SUCCESS)
4241 goto done;
4243 size = MAX_PATH;
4244 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4245 NULL, guids, &size);
4246 if (r != ERROR_SUCCESS)
4247 goto done;
4249 ptr = strchrW(guids, ';');
4250 if (ptr) *ptr = 0;
4251 squash_guid(guids, packcode);
4252 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4254 done:
4255 MsiCloseHandle(suminfo);
4256 return ERROR_SUCCESS;
4259 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4261 UINT r;
4262 HKEY hkey;
4263 LPWSTR upgrade;
4264 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4266 upgrade = msi_dup_property(package->db, szUpgradeCode);
4267 if (!upgrade)
4268 return ERROR_SUCCESS;
4270 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4271 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4272 else
4273 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4275 if (r != ERROR_SUCCESS)
4277 WARN("failed to open upgrade code key\n");
4278 msi_free(upgrade);
4279 return ERROR_SUCCESS;
4281 squash_guid(package->ProductCode, squashed_pc);
4282 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4283 RegCloseKey(hkey);
4284 msi_free(upgrade);
4285 return ERROR_SUCCESS;
4288 static BOOL msi_check_publish(MSIPACKAGE *package)
4290 MSIFEATURE *feature;
4292 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4294 feature->Action = msi_get_feature_action( package, feature );
4295 if (feature->Action == INSTALLSTATE_LOCAL)
4296 return TRUE;
4299 return FALSE;
4302 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4304 MSIFEATURE *feature;
4306 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4308 feature->Action = msi_get_feature_action( package, feature );
4309 if (feature->Action != INSTALLSTATE_ABSENT)
4310 return FALSE;
4313 return TRUE;
4316 static UINT msi_publish_patches( MSIPACKAGE *package )
4318 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4319 WCHAR patch_squashed[GUID_SIZE];
4320 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4321 LONG res;
4322 MSIPATCHINFO *patch;
4323 UINT r;
4324 WCHAR *p, *all_patches = NULL;
4325 DWORD len = 0;
4327 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4328 if (r != ERROR_SUCCESS)
4329 return ERROR_FUNCTION_FAILED;
4331 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4332 if (res != ERROR_SUCCESS)
4334 r = ERROR_FUNCTION_FAILED;
4335 goto done;
4338 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4339 if (r != ERROR_SUCCESS)
4340 goto done;
4342 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4344 squash_guid( patch->patchcode, patch_squashed );
4345 len += strlenW( patch_squashed ) + 1;
4348 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4349 if (!all_patches)
4350 goto done;
4352 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4354 HKEY patch_key;
4356 squash_guid( patch->patchcode, p );
4357 p += strlenW( p ) + 1;
4359 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4360 (const BYTE *)patch->transforms,
4361 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4362 if (res != ERROR_SUCCESS)
4363 goto done;
4365 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4366 if (r != ERROR_SUCCESS)
4367 goto done;
4369 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4370 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4371 RegCloseKey( patch_key );
4372 if (res != ERROR_SUCCESS)
4373 goto done;
4375 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4377 res = GetLastError();
4378 ERR("Unable to copy patch package %d\n", res);
4379 goto done;
4381 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4382 if (res != ERROR_SUCCESS)
4383 goto done;
4385 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4386 RegCloseKey( patch_key );
4387 if (res != ERROR_SUCCESS)
4388 goto done;
4391 all_patches[len] = 0;
4392 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4393 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4394 if (res != ERROR_SUCCESS)
4395 goto done;
4397 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4398 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4399 if (res != ERROR_SUCCESS)
4400 r = ERROR_FUNCTION_FAILED;
4402 done:
4403 RegCloseKey( product_patches_key );
4404 RegCloseKey( patches_key );
4405 RegCloseKey( product_key );
4406 msi_free( all_patches );
4407 return r;
4410 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4412 UINT rc;
4413 HKEY hukey = NULL, hudkey = NULL;
4414 MSIRECORD *uirow;
4416 if (!list_empty(&package->patches))
4418 rc = msi_publish_patches(package);
4419 if (rc != ERROR_SUCCESS)
4420 goto end;
4423 /* FIXME: also need to publish if the product is in advertise mode */
4424 if (!msi_check_publish(package))
4425 return ERROR_SUCCESS;
4427 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4428 &hukey, TRUE);
4429 if (rc != ERROR_SUCCESS)
4430 goto end;
4432 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4433 NULL, &hudkey, TRUE);
4434 if (rc != ERROR_SUCCESS)
4435 goto end;
4437 rc = msi_publish_upgrade_code(package);
4438 if (rc != ERROR_SUCCESS)
4439 goto end;
4441 rc = msi_publish_product_properties(package, hukey);
4442 if (rc != ERROR_SUCCESS)
4443 goto end;
4445 rc = msi_publish_sourcelist(package, hukey);
4446 if (rc != ERROR_SUCCESS)
4447 goto end;
4449 rc = msi_publish_icons(package);
4451 end:
4452 uirow = MSI_CreateRecord( 1 );
4453 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4454 msi_ui_actiondata( package, szPublishProduct, uirow );
4455 msiobj_release( &uirow->hdr );
4457 RegCloseKey(hukey);
4458 RegCloseKey(hudkey);
4459 return rc;
4462 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4464 WCHAR *filename, *ptr, *folder, *ret;
4465 const WCHAR *dirprop;
4467 filename = msi_dup_record_field( row, 2 );
4468 if (filename && (ptr = strchrW( filename, '|' )))
4469 ptr++;
4470 else
4471 ptr = filename;
4473 dirprop = MSI_RecordGetString( row, 3 );
4474 if (dirprop)
4476 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4477 if (!folder) folder = msi_dup_property( package->db, dirprop );
4479 else
4480 folder = msi_dup_property( package->db, szWindowsFolder );
4482 if (!folder)
4484 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4485 msi_free( filename );
4486 return NULL;
4489 ret = msi_build_directory_name( 2, folder, ptr );
4491 msi_free( filename );
4492 msi_free( folder );
4493 return ret;
4496 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4498 MSIPACKAGE *package = param;
4499 LPCWSTR component, section, key, value, identifier;
4500 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4501 MSIRECORD * uirow;
4502 INT action;
4503 MSICOMPONENT *comp;
4505 component = MSI_RecordGetString(row, 8);
4506 comp = msi_get_loaded_component(package,component);
4507 if (!comp)
4508 return ERROR_SUCCESS;
4510 comp->Action = msi_get_component_action( package, comp );
4511 if (comp->Action != INSTALLSTATE_LOCAL)
4513 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4514 return ERROR_SUCCESS;
4517 identifier = MSI_RecordGetString(row,1);
4518 section = MSI_RecordGetString(row,4);
4519 key = MSI_RecordGetString(row,5);
4520 value = MSI_RecordGetString(row,6);
4521 action = MSI_RecordGetInteger(row,7);
4523 deformat_string(package,section,&deformated_section);
4524 deformat_string(package,key,&deformated_key);
4525 deformat_string(package,value,&deformated_value);
4527 fullname = get_ini_file_name(package, row);
4529 if (action == 0)
4531 TRACE("Adding value %s to section %s in %s\n",
4532 debugstr_w(deformated_key), debugstr_w(deformated_section),
4533 debugstr_w(fullname));
4534 WritePrivateProfileStringW(deformated_section, deformated_key,
4535 deformated_value, fullname);
4537 else if (action == 1)
4539 WCHAR returned[10];
4540 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4541 returned, 10, fullname);
4542 if (returned[0] == 0)
4544 TRACE("Adding value %s to section %s in %s\n",
4545 debugstr_w(deformated_key), debugstr_w(deformated_section),
4546 debugstr_w(fullname));
4548 WritePrivateProfileStringW(deformated_section, deformated_key,
4549 deformated_value, fullname);
4552 else if (action == 3)
4553 FIXME("Append to existing section not yet implemented\n");
4555 uirow = MSI_CreateRecord(4);
4556 MSI_RecordSetStringW(uirow,1,identifier);
4557 MSI_RecordSetStringW(uirow,2,deformated_section);
4558 MSI_RecordSetStringW(uirow,3,deformated_key);
4559 MSI_RecordSetStringW(uirow,4,deformated_value);
4560 msi_ui_actiondata( package, szWriteIniValues, uirow );
4561 msiobj_release( &uirow->hdr );
4563 msi_free(fullname);
4564 msi_free(deformated_key);
4565 msi_free(deformated_value);
4566 msi_free(deformated_section);
4567 return ERROR_SUCCESS;
4570 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4572 static const WCHAR query[] = {
4573 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4574 '`','I','n','i','F','i','l','e','`',0};
4575 MSIQUERY *view;
4576 UINT rc;
4578 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4579 if (rc != ERROR_SUCCESS)
4580 return ERROR_SUCCESS;
4582 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4583 msiobj_release(&view->hdr);
4584 return rc;
4587 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4589 MSIPACKAGE *package = param;
4590 LPCWSTR component, section, key, value, identifier;
4591 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4592 MSICOMPONENT *comp;
4593 MSIRECORD *uirow;
4594 INT action;
4596 component = MSI_RecordGetString( row, 8 );
4597 comp = msi_get_loaded_component( package, component );
4598 if (!comp)
4599 return ERROR_SUCCESS;
4601 comp->Action = msi_get_component_action( package, comp );
4602 if (comp->Action != INSTALLSTATE_ABSENT)
4604 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4605 return ERROR_SUCCESS;
4608 identifier = MSI_RecordGetString( row, 1 );
4609 section = MSI_RecordGetString( row, 4 );
4610 key = MSI_RecordGetString( row, 5 );
4611 value = MSI_RecordGetString( row, 6 );
4612 action = MSI_RecordGetInteger( row, 7 );
4614 deformat_string( package, section, &deformated_section );
4615 deformat_string( package, key, &deformated_key );
4616 deformat_string( package, value, &deformated_value );
4618 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4620 filename = get_ini_file_name( package, row );
4622 TRACE("Removing key %s from section %s in %s\n",
4623 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4625 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4627 WARN("Unable to remove key %u\n", GetLastError());
4629 msi_free( filename );
4631 else
4632 FIXME("Unsupported action %d\n", action);
4635 uirow = MSI_CreateRecord( 4 );
4636 MSI_RecordSetStringW( uirow, 1, identifier );
4637 MSI_RecordSetStringW( uirow, 2, deformated_section );
4638 MSI_RecordSetStringW( uirow, 3, deformated_key );
4639 MSI_RecordSetStringW( uirow, 4, deformated_value );
4640 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4641 msiobj_release( &uirow->hdr );
4643 msi_free( deformated_key );
4644 msi_free( deformated_value );
4645 msi_free( deformated_section );
4646 return ERROR_SUCCESS;
4649 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4651 MSIPACKAGE *package = param;
4652 LPCWSTR component, section, key, value, identifier;
4653 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4654 MSICOMPONENT *comp;
4655 MSIRECORD *uirow;
4656 INT action;
4658 component = MSI_RecordGetString( row, 8 );
4659 comp = msi_get_loaded_component( package, component );
4660 if (!comp)
4661 return ERROR_SUCCESS;
4663 comp->Action = msi_get_component_action( package, comp );
4664 if (comp->Action != INSTALLSTATE_LOCAL)
4666 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4667 return ERROR_SUCCESS;
4670 identifier = MSI_RecordGetString( row, 1 );
4671 section = MSI_RecordGetString( row, 4 );
4672 key = MSI_RecordGetString( row, 5 );
4673 value = MSI_RecordGetString( row, 6 );
4674 action = MSI_RecordGetInteger( row, 7 );
4676 deformat_string( package, section, &deformated_section );
4677 deformat_string( package, key, &deformated_key );
4678 deformat_string( package, value, &deformated_value );
4680 if (action == msidbIniFileActionRemoveLine)
4682 filename = get_ini_file_name( package, row );
4684 TRACE("Removing key %s from section %s in %s\n",
4685 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4687 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4689 WARN("Unable to remove key %u\n", GetLastError());
4691 msi_free( filename );
4693 else
4694 FIXME("Unsupported action %d\n", action);
4696 uirow = MSI_CreateRecord( 4 );
4697 MSI_RecordSetStringW( uirow, 1, identifier );
4698 MSI_RecordSetStringW( uirow, 2, deformated_section );
4699 MSI_RecordSetStringW( uirow, 3, deformated_key );
4700 MSI_RecordSetStringW( uirow, 4, deformated_value );
4701 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4702 msiobj_release( &uirow->hdr );
4704 msi_free( deformated_key );
4705 msi_free( deformated_value );
4706 msi_free( deformated_section );
4707 return ERROR_SUCCESS;
4710 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4712 static const WCHAR query[] = {
4713 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4714 '`','I','n','i','F','i','l','e','`',0};
4715 static const WCHAR remove_query[] = {
4716 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4717 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4718 MSIQUERY *view;
4719 UINT rc;
4721 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4722 if (rc == ERROR_SUCCESS)
4724 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4725 msiobj_release( &view->hdr );
4726 if (rc != ERROR_SUCCESS)
4727 return rc;
4729 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4730 if (rc == ERROR_SUCCESS)
4732 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4733 msiobj_release( &view->hdr );
4734 if (rc != ERROR_SUCCESS)
4735 return rc;
4737 return ERROR_SUCCESS;
4740 static void register_dll( const WCHAR *dll, BOOL unregister )
4742 static const WCHAR regW[] =
4743 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4744 static const WCHAR unregW[] =
4745 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4746 PROCESS_INFORMATION pi;
4747 STARTUPINFOW si;
4748 WCHAR *cmd;
4750 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4752 if (unregister) sprintfW( cmd, unregW, dll );
4753 else sprintfW( cmd, regW, dll );
4755 memset( &si, 0, sizeof(STARTUPINFOW) );
4756 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4758 CloseHandle( pi.hThread );
4759 msi_dialog_check_messages( pi.hProcess );
4760 CloseHandle( pi.hProcess );
4762 msi_free( cmd );
4765 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4767 MSIPACKAGE *package = param;
4768 LPCWSTR filename;
4769 MSIFILE *file;
4770 MSIRECORD *uirow;
4772 filename = MSI_RecordGetString( row, 1 );
4773 file = msi_get_loaded_file( package, filename );
4774 if (!file)
4776 WARN("unable to find file %s\n", debugstr_w(filename));
4777 return ERROR_SUCCESS;
4779 file->Component->Action = msi_get_component_action( package, file->Component );
4780 if (file->Component->Action != INSTALLSTATE_LOCAL)
4782 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4783 return ERROR_SUCCESS;
4786 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4787 register_dll( file->TargetPath, FALSE );
4789 uirow = MSI_CreateRecord( 2 );
4790 MSI_RecordSetStringW( uirow, 1, file->File );
4791 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4792 msi_ui_actiondata( package, szSelfRegModules, uirow );
4793 msiobj_release( &uirow->hdr );
4795 return ERROR_SUCCESS;
4798 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4800 static const WCHAR query[] = {
4801 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4802 '`','S','e','l','f','R','e','g','`',0};
4803 MSIQUERY *view;
4804 UINT rc;
4806 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4807 if (rc != ERROR_SUCCESS)
4808 return ERROR_SUCCESS;
4810 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4811 msiobj_release(&view->hdr);
4812 return rc;
4815 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4817 MSIPACKAGE *package = param;
4818 LPCWSTR filename;
4819 MSIFILE *file;
4820 MSIRECORD *uirow;
4822 filename = MSI_RecordGetString( row, 1 );
4823 file = msi_get_loaded_file( package, filename );
4824 if (!file)
4826 WARN("unable to find file %s\n", debugstr_w(filename));
4827 return ERROR_SUCCESS;
4829 file->Component->Action = msi_get_component_action( package, file->Component );
4830 if (file->Component->Action != INSTALLSTATE_ABSENT)
4832 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4833 return ERROR_SUCCESS;
4836 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4837 register_dll( file->TargetPath, TRUE );
4839 uirow = MSI_CreateRecord( 2 );
4840 MSI_RecordSetStringW( uirow, 1, file->File );
4841 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4842 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4843 msiobj_release( &uirow->hdr );
4845 return ERROR_SUCCESS;
4848 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4850 static const WCHAR query[] = {
4851 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4852 '`','S','e','l','f','R','e','g','`',0};
4853 MSIQUERY *view;
4854 UINT rc;
4856 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4857 if (rc != ERROR_SUCCESS)
4858 return ERROR_SUCCESS;
4860 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4861 msiobj_release( &view->hdr );
4862 return rc;
4865 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4867 MSIFEATURE *feature;
4868 UINT rc;
4869 HKEY hkey = NULL, userdata = NULL;
4871 if (!msi_check_publish(package))
4872 return ERROR_SUCCESS;
4874 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4875 &hkey, TRUE);
4876 if (rc != ERROR_SUCCESS)
4877 goto end;
4879 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4880 &userdata, TRUE);
4881 if (rc != ERROR_SUCCESS)
4882 goto end;
4884 /* here the guids are base 85 encoded */
4885 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4887 ComponentList *cl;
4888 LPWSTR data = NULL;
4889 GUID clsid;
4890 INT size;
4891 BOOL absent = FALSE;
4892 MSIRECORD *uirow;
4894 if (feature->Level <= 0) continue;
4896 if (feature->Action != INSTALLSTATE_LOCAL &&
4897 feature->Action != INSTALLSTATE_SOURCE &&
4898 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4900 size = 1;
4901 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4903 size += 21;
4905 if (feature->Feature_Parent)
4906 size += strlenW( feature->Feature_Parent )+2;
4908 data = msi_alloc(size * sizeof(WCHAR));
4910 data[0] = 0;
4911 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4913 MSICOMPONENT* component = cl->component;
4914 WCHAR buf[21];
4916 buf[0] = 0;
4917 if (component->ComponentId)
4919 TRACE("From %s\n",debugstr_w(component->ComponentId));
4920 CLSIDFromString(component->ComponentId, &clsid);
4921 encode_base85_guid(&clsid,buf);
4922 TRACE("to %s\n",debugstr_w(buf));
4923 strcatW(data,buf);
4927 if (feature->Feature_Parent)
4929 static const WCHAR sep[] = {'\2',0};
4930 strcatW(data,sep);
4931 strcatW(data,feature->Feature_Parent);
4934 msi_reg_set_val_str( userdata, feature->Feature, data );
4935 msi_free(data);
4937 size = 0;
4938 if (feature->Feature_Parent)
4939 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4940 if (!absent)
4942 size += sizeof(WCHAR);
4943 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4944 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4946 else
4948 size += 2*sizeof(WCHAR);
4949 data = msi_alloc(size);
4950 data[0] = 0x6;
4951 data[1] = 0;
4952 if (feature->Feature_Parent)
4953 strcpyW( &data[1], feature->Feature_Parent );
4954 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4955 (LPBYTE)data,size);
4956 msi_free(data);
4959 /* the UI chunk */
4960 uirow = MSI_CreateRecord( 1 );
4961 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4962 msi_ui_actiondata( package, szPublishFeatures, uirow );
4963 msiobj_release( &uirow->hdr );
4964 /* FIXME: call msi_ui_progress? */
4967 end:
4968 RegCloseKey(hkey);
4969 RegCloseKey(userdata);
4970 return rc;
4973 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4975 UINT r;
4976 HKEY hkey;
4977 MSIRECORD *uirow;
4979 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4981 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4982 &hkey, FALSE);
4983 if (r == ERROR_SUCCESS)
4985 RegDeleteValueW(hkey, feature->Feature);
4986 RegCloseKey(hkey);
4989 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4990 &hkey, FALSE);
4991 if (r == ERROR_SUCCESS)
4993 RegDeleteValueW(hkey, feature->Feature);
4994 RegCloseKey(hkey);
4997 uirow = MSI_CreateRecord( 1 );
4998 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4999 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5000 msiobj_release( &uirow->hdr );
5002 return ERROR_SUCCESS;
5005 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5007 MSIFEATURE *feature;
5009 if (!msi_check_unpublish(package))
5010 return ERROR_SUCCESS;
5012 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5014 msi_unpublish_feature(package, feature);
5017 return ERROR_SUCCESS;
5020 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5022 SYSTEMTIME systime;
5023 DWORD size, langid;
5024 WCHAR date[9], *val, *buffer;
5025 const WCHAR *prop, *key;
5027 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5028 static const WCHAR modpath_fmt[] =
5029 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5030 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5031 static const WCHAR szModifyPath[] =
5032 {'M','o','d','i','f','y','P','a','t','h',0};
5033 static const WCHAR szUninstallString[] =
5034 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5035 static const WCHAR szEstimatedSize[] =
5036 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5037 static const WCHAR szDisplayVersion[] =
5038 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5039 static const WCHAR szInstallSource[] =
5040 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5041 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5042 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5043 static const WCHAR szAuthorizedCDFPrefix[] =
5044 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5045 static const WCHAR szARPCONTACT[] =
5046 {'A','R','P','C','O','N','T','A','C','T',0};
5047 static const WCHAR szContact[] =
5048 {'C','o','n','t','a','c','t',0};
5049 static const WCHAR szARPCOMMENTS[] =
5050 {'A','R','P','C','O','M','M','E','N','T','S',0};
5051 static const WCHAR szComments[] =
5052 {'C','o','m','m','e','n','t','s',0};
5053 static const WCHAR szProductName[] =
5054 {'P','r','o','d','u','c','t','N','a','m','e',0};
5055 static const WCHAR szDisplayName[] =
5056 {'D','i','s','p','l','a','y','N','a','m','e',0};
5057 static const WCHAR szARPHELPLINK[] =
5058 {'A','R','P','H','E','L','P','L','I','N','K',0};
5059 static const WCHAR szHelpLink[] =
5060 {'H','e','l','p','L','i','n','k',0};
5061 static const WCHAR szARPHELPTELEPHONE[] =
5062 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5063 static const WCHAR szHelpTelephone[] =
5064 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5065 static const WCHAR szARPINSTALLLOCATION[] =
5066 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5067 static const WCHAR szManufacturer[] =
5068 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5069 static const WCHAR szPublisher[] =
5070 {'P','u','b','l','i','s','h','e','r',0};
5071 static const WCHAR szARPREADME[] =
5072 {'A','R','P','R','E','A','D','M','E',0};
5073 static const WCHAR szReadme[] =
5074 {'R','e','a','d','M','e',0};
5075 static const WCHAR szARPSIZE[] =
5076 {'A','R','P','S','I','Z','E',0};
5077 static const WCHAR szSize[] =
5078 {'S','i','z','e',0};
5079 static const WCHAR szARPURLINFOABOUT[] =
5080 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5081 static const WCHAR szURLInfoAbout[] =
5082 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5083 static const WCHAR szARPURLUPDATEINFO[] =
5084 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5085 static const WCHAR szURLUpdateInfo[] =
5086 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5087 static const WCHAR szARPSYSTEMCOMPONENT[] =
5088 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5089 static const WCHAR szSystemComponent[] =
5090 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5092 static const WCHAR *propval[] = {
5093 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5094 szARPCONTACT, szContact,
5095 szARPCOMMENTS, szComments,
5096 szProductName, szDisplayName,
5097 szARPHELPLINK, szHelpLink,
5098 szARPHELPTELEPHONE, szHelpTelephone,
5099 szARPINSTALLLOCATION, szInstallLocation,
5100 szSourceDir, szInstallSource,
5101 szManufacturer, szPublisher,
5102 szARPREADME, szReadme,
5103 szARPSIZE, szSize,
5104 szARPURLINFOABOUT, szURLInfoAbout,
5105 szARPURLUPDATEINFO, szURLUpdateInfo,
5106 NULL
5108 const WCHAR **p = propval;
5110 while (*p)
5112 prop = *p++;
5113 key = *p++;
5114 val = msi_dup_property(package->db, prop);
5115 msi_reg_set_val_str(hkey, key, val);
5116 msi_free(val);
5119 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5120 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5122 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5124 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5125 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5126 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5127 msi_free(buffer);
5129 /* FIXME: Write real Estimated Size when we have it */
5130 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5132 GetLocalTime(&systime);
5133 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5134 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5136 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5137 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5139 buffer = msi_dup_property(package->db, szProductVersion);
5140 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5141 if (buffer)
5143 DWORD verdword = msi_version_str_to_dword(buffer);
5145 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5146 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5147 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5148 msi_free(buffer);
5151 return ERROR_SUCCESS;
5154 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5156 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5157 MSIRECORD *uirow;
5158 LPWSTR upgrade_code;
5159 HKEY hkey, props, upgrade_key;
5160 UINT rc;
5162 /* FIXME: also need to publish if the product is in advertise mode */
5163 if (!msi_check_publish(package))
5164 return ERROR_SUCCESS;
5166 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5167 if (rc != ERROR_SUCCESS)
5168 return rc;
5170 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5171 if (rc != ERROR_SUCCESS)
5172 goto done;
5174 rc = msi_publish_install_properties(package, hkey);
5175 if (rc != ERROR_SUCCESS)
5176 goto done;
5178 rc = msi_publish_install_properties(package, props);
5179 if (rc != ERROR_SUCCESS)
5180 goto done;
5182 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5183 if (upgrade_code)
5185 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5186 if (rc == ERROR_SUCCESS)
5188 squash_guid( package->ProductCode, squashed_pc );
5189 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5190 RegCloseKey( upgrade_key );
5192 msi_free( upgrade_code );
5194 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5195 package->delete_on_close = FALSE;
5197 done:
5198 uirow = MSI_CreateRecord( 1 );
5199 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5200 msi_ui_actiondata( package, szRegisterProduct, uirow );
5201 msiobj_release( &uirow->hdr );
5203 RegCloseKey(hkey);
5204 return ERROR_SUCCESS;
5207 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5209 return execute_script(package, SCRIPT_INSTALL);
5212 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5214 MSIPACKAGE *package = param;
5215 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5216 WCHAR *p, *icon_path;
5218 if (!icon) return ERROR_SUCCESS;
5219 if ((icon_path = msi_build_icon_path( package, icon )))
5221 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5222 DeleteFileW( icon_path );
5223 if ((p = strrchrW( icon_path, '\\' )))
5225 *p = 0;
5226 RemoveDirectoryW( icon_path );
5228 msi_free( icon_path );
5230 return ERROR_SUCCESS;
5233 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5235 static const WCHAR query[]= {
5236 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5237 MSIQUERY *view;
5238 UINT r;
5240 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5241 if (r == ERROR_SUCCESS)
5243 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5244 msiobj_release( &view->hdr );
5245 if (r != ERROR_SUCCESS)
5246 return r;
5248 return ERROR_SUCCESS;
5251 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5253 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5254 WCHAR *upgrade, **features;
5255 BOOL full_uninstall = TRUE;
5256 MSIFEATURE *feature;
5257 MSIPATCHINFO *patch;
5258 UINT i;
5260 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5262 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5264 features = msi_split_string( remove, ',' );
5265 for (i = 0; features && features[i]; i++)
5267 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5269 msi_free(features);
5271 if (!full_uninstall)
5272 return ERROR_SUCCESS;
5274 MSIREG_DeleteProductKey(package->ProductCode);
5275 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5276 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5278 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5279 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5280 MSIREG_DeleteUserProductKey(package->ProductCode);
5281 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5283 upgrade = msi_dup_property(package->db, szUpgradeCode);
5284 if (upgrade)
5286 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5287 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5288 msi_free(upgrade);
5291 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5293 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5294 if (!strcmpW( package->ProductCode, patch->products ))
5296 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5297 patch->delete_on_close = TRUE;
5299 /* FIXME: remove local patch package if this is the last product */
5301 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5302 package->delete_on_close = TRUE;
5304 msi_unpublish_icons( package );
5305 return ERROR_SUCCESS;
5308 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5310 UINT rc;
5311 WCHAR *remove;
5313 /* first do the same as an InstallExecute */
5314 rc = ACTION_InstallExecute(package);
5315 if (rc != ERROR_SUCCESS)
5316 return rc;
5318 /* then handle commit actions */
5319 rc = execute_script(package, SCRIPT_COMMIT);
5320 if (rc != ERROR_SUCCESS)
5321 return rc;
5323 remove = msi_dup_property(package->db, szRemove);
5324 rc = msi_unpublish_product(package, remove);
5325 msi_free(remove);
5326 return rc;
5329 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5331 static const WCHAR RunOnce[] = {
5332 'S','o','f','t','w','a','r','e','\\',
5333 'M','i','c','r','o','s','o','f','t','\\',
5334 'W','i','n','d','o','w','s','\\',
5335 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5336 'R','u','n','O','n','c','e',0};
5337 static const WCHAR InstallRunOnce[] = {
5338 'S','o','f','t','w','a','r','e','\\',
5339 'M','i','c','r','o','s','o','f','t','\\',
5340 'W','i','n','d','o','w','s','\\',
5341 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5342 'I','n','s','t','a','l','l','e','r','\\',
5343 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5345 static const WCHAR msiexec_fmt[] = {
5346 '%','s',
5347 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5348 '\"','%','s','\"',0};
5349 static const WCHAR install_fmt[] = {
5350 '/','I',' ','\"','%','s','\"',' ',
5351 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5352 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5353 WCHAR buffer[256], sysdir[MAX_PATH];
5354 HKEY hkey;
5355 WCHAR squished_pc[100];
5357 squash_guid(package->ProductCode,squished_pc);
5359 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5360 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5361 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5362 squished_pc);
5364 msi_reg_set_val_str( hkey, squished_pc, buffer );
5365 RegCloseKey(hkey);
5367 TRACE("Reboot command %s\n",debugstr_w(buffer));
5369 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5370 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5372 msi_reg_set_val_str( hkey, squished_pc, buffer );
5373 RegCloseKey(hkey);
5375 return ERROR_INSTALL_SUSPEND;
5378 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5380 static const WCHAR query[] =
5381 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5382 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5383 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5384 MSIRECORD *rec, *row;
5385 DWORD i, size = 0;
5386 va_list va;
5387 const WCHAR *str;
5388 WCHAR *data;
5390 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5392 rec = MSI_CreateRecord( count + 2 );
5393 str = MSI_RecordGetString( row, 1 );
5394 MSI_RecordSetStringW( rec, 0, str );
5395 msiobj_release( &row->hdr );
5396 MSI_RecordSetInteger( rec, 1, error );
5398 va_start( va, count );
5399 for (i = 0; i < count; i++)
5401 str = va_arg( va, const WCHAR *);
5402 MSI_RecordSetStringW( rec, i + 2, str );
5404 va_end( va );
5406 MSI_FormatRecordW( package, rec, NULL, &size );
5407 size++;
5408 data = msi_alloc( size * sizeof(WCHAR) );
5409 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5410 else data[0] = 0;
5411 msiobj_release( &rec->hdr );
5412 return data;
5415 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5417 DWORD attrib;
5418 UINT rc;
5421 * We are currently doing what should be done here in the top level Install
5422 * however for Administrative and uninstalls this step will be needed
5424 if (!package->PackagePath)
5425 return ERROR_SUCCESS;
5427 msi_set_sourcedir_props(package, TRUE);
5429 attrib = GetFileAttributesW(package->db->path);
5430 if (attrib == INVALID_FILE_ATTRIBUTES)
5432 LPWSTR prompt, msg;
5433 DWORD size = 0;
5435 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5436 package->Context, MSICODE_PRODUCT,
5437 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5438 if (rc == ERROR_MORE_DATA)
5440 prompt = msi_alloc(size * sizeof(WCHAR));
5441 MsiSourceListGetInfoW(package->ProductCode, NULL,
5442 package->Context, MSICODE_PRODUCT,
5443 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5445 else
5446 prompt = strdupW(package->db->path);
5448 msg = msi_build_error_string(package, 1302, 1, prompt);
5449 msi_free(prompt);
5450 while(attrib == INVALID_FILE_ATTRIBUTES)
5452 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5453 if (rc == IDCANCEL)
5455 msi_free(msg);
5456 return ERROR_INSTALL_USEREXIT;
5458 attrib = GetFileAttributesW(package->db->path);
5460 msi_free(msg);
5461 rc = ERROR_SUCCESS;
5463 else
5464 return ERROR_SUCCESS;
5466 return rc;
5469 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5471 HKEY hkey = 0;
5472 LPWSTR buffer, productid = NULL;
5473 UINT i, rc = ERROR_SUCCESS;
5474 MSIRECORD *uirow;
5476 static const WCHAR szPropKeys[][80] =
5478 {'P','r','o','d','u','c','t','I','D',0},
5479 {'U','S','E','R','N','A','M','E',0},
5480 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5481 {0},
5484 static const WCHAR szRegKeys[][80] =
5486 {'P','r','o','d','u','c','t','I','D',0},
5487 {'R','e','g','O','w','n','e','r',0},
5488 {'R','e','g','C','o','m','p','a','n','y',0},
5489 {0},
5492 if (msi_check_unpublish(package))
5494 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5495 goto end;
5498 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5499 if (!productid)
5500 goto end;
5502 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5503 NULL, &hkey, TRUE);
5504 if (rc != ERROR_SUCCESS)
5505 goto end;
5507 for( i = 0; szPropKeys[i][0]; i++ )
5509 buffer = msi_dup_property( package->db, szPropKeys[i] );
5510 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5511 msi_free( buffer );
5514 end:
5515 uirow = MSI_CreateRecord( 1 );
5516 MSI_RecordSetStringW( uirow, 1, productid );
5517 msi_ui_actiondata( package, szRegisterUser, uirow );
5518 msiobj_release( &uirow->hdr );
5520 msi_free(productid);
5521 RegCloseKey(hkey);
5522 return rc;
5526 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5528 UINT rc;
5530 package->script->InWhatSequence |= SEQUENCE_EXEC;
5531 rc = ACTION_ProcessExecSequence(package,FALSE);
5532 return rc;
5535 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5537 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5538 WCHAR productid_85[21], component_85[21], *ret;
5539 GUID clsid;
5540 DWORD sz;
5542 /* > is used if there is a component GUID and < if not. */
5544 productid_85[0] = 0;
5545 component_85[0] = 0;
5546 CLSIDFromString( package->ProductCode, &clsid );
5548 encode_base85_guid( &clsid, productid_85 );
5549 if (component)
5551 CLSIDFromString( component->ComponentId, &clsid );
5552 encode_base85_guid( &clsid, component_85 );
5555 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5556 debugstr_w(component_85));
5558 sz = 20 + strlenW( feature ) + 20 + 3;
5559 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5560 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5561 return ret;
5564 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5566 MSIPACKAGE *package = param;
5567 LPCWSTR compgroupid, component, feature, qualifier, text;
5568 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5569 HKEY hkey = NULL;
5570 UINT rc;
5571 MSICOMPONENT *comp;
5572 MSIFEATURE *feat;
5573 DWORD sz;
5574 MSIRECORD *uirow;
5575 int len;
5577 feature = MSI_RecordGetString(rec, 5);
5578 feat = msi_get_loaded_feature(package, feature);
5579 if (!feat)
5580 return ERROR_SUCCESS;
5582 feat->Action = msi_get_feature_action( package, feat );
5583 if (feat->Action != INSTALLSTATE_LOCAL &&
5584 feat->Action != INSTALLSTATE_SOURCE &&
5585 feat->Action != INSTALLSTATE_ADVERTISED)
5587 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5588 return ERROR_SUCCESS;
5591 component = MSI_RecordGetString(rec, 3);
5592 comp = msi_get_loaded_component(package, component);
5593 if (!comp)
5594 return ERROR_SUCCESS;
5596 compgroupid = MSI_RecordGetString(rec,1);
5597 qualifier = MSI_RecordGetString(rec,2);
5599 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5600 if (rc != ERROR_SUCCESS)
5601 goto end;
5603 advertise = msi_create_component_advertise_string( package, comp, feature );
5604 text = MSI_RecordGetString( rec, 4 );
5605 if (text)
5607 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5608 strcpyW( p, advertise );
5609 strcatW( p, text );
5610 msi_free( advertise );
5611 advertise = p;
5613 existing = msi_reg_get_val_str( hkey, qualifier );
5615 sz = strlenW( advertise ) + 1;
5616 if (existing)
5618 for (p = existing; *p; p += len)
5620 len = strlenW( p ) + 1;
5621 if (strcmpW( advertise, p )) sz += len;
5624 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5626 rc = ERROR_OUTOFMEMORY;
5627 goto end;
5629 q = output;
5630 if (existing)
5632 for (p = existing; *p; p += len)
5634 len = strlenW( p ) + 1;
5635 if (strcmpW( advertise, p ))
5637 memcpy( q, p, len * sizeof(WCHAR) );
5638 q += len;
5642 strcpyW( q, advertise );
5643 q[strlenW( q ) + 1] = 0;
5645 msi_reg_set_val_multi_str( hkey, qualifier, output );
5647 end:
5648 RegCloseKey(hkey);
5649 msi_free( output );
5650 msi_free( advertise );
5651 msi_free( existing );
5653 /* the UI chunk */
5654 uirow = MSI_CreateRecord( 2 );
5655 MSI_RecordSetStringW( uirow, 1, compgroupid );
5656 MSI_RecordSetStringW( uirow, 2, qualifier);
5657 msi_ui_actiondata( package, szPublishComponents, uirow );
5658 msiobj_release( &uirow->hdr );
5659 /* FIXME: call ui_progress? */
5661 return rc;
5665 * At present I am ignorning the advertised components part of this and only
5666 * focusing on the qualified component sets
5668 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5670 static const WCHAR query[] = {
5671 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5672 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5673 MSIQUERY *view;
5674 UINT rc;
5676 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5677 if (rc != ERROR_SUCCESS)
5678 return ERROR_SUCCESS;
5680 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5681 msiobj_release(&view->hdr);
5682 return rc;
5685 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5687 static const WCHAR szInstallerComponents[] = {
5688 'S','o','f','t','w','a','r','e','\\',
5689 'M','i','c','r','o','s','o','f','t','\\',
5690 'I','n','s','t','a','l','l','e','r','\\',
5691 'C','o','m','p','o','n','e','n','t','s','\\',0};
5693 MSIPACKAGE *package = param;
5694 LPCWSTR compgroupid, component, feature, qualifier;
5695 MSICOMPONENT *comp;
5696 MSIFEATURE *feat;
5697 MSIRECORD *uirow;
5698 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5699 LONG res;
5701 feature = MSI_RecordGetString( rec, 5 );
5702 feat = msi_get_loaded_feature( package, feature );
5703 if (!feat)
5704 return ERROR_SUCCESS;
5706 feat->Action = msi_get_feature_action( package, feat );
5707 if (feat->Action != INSTALLSTATE_ABSENT)
5709 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5710 return ERROR_SUCCESS;
5713 component = MSI_RecordGetString( rec, 3 );
5714 comp = msi_get_loaded_component( package, component );
5715 if (!comp)
5716 return ERROR_SUCCESS;
5718 compgroupid = MSI_RecordGetString( rec, 1 );
5719 qualifier = MSI_RecordGetString( rec, 2 );
5721 squash_guid( compgroupid, squashed );
5722 strcpyW( keypath, szInstallerComponents );
5723 strcatW( keypath, squashed );
5725 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5726 if (res != ERROR_SUCCESS)
5728 WARN("Unable to delete component key %d\n", res);
5731 uirow = MSI_CreateRecord( 2 );
5732 MSI_RecordSetStringW( uirow, 1, compgroupid );
5733 MSI_RecordSetStringW( uirow, 2, qualifier );
5734 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5735 msiobj_release( &uirow->hdr );
5737 return ERROR_SUCCESS;
5740 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5742 static const WCHAR query[] = {
5743 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5744 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5745 MSIQUERY *view;
5746 UINT rc;
5748 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5749 if (rc != ERROR_SUCCESS)
5750 return ERROR_SUCCESS;
5752 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5753 msiobj_release( &view->hdr );
5754 return rc;
5757 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5759 static const WCHAR query[] =
5760 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5761 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5762 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5763 MSIPACKAGE *package = param;
5764 MSICOMPONENT *component;
5765 MSIRECORD *row;
5766 MSIFILE *file;
5767 SC_HANDLE hscm = NULL, service = NULL;
5768 LPCWSTR comp, key;
5769 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5770 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5771 DWORD serv_type, start_type, err_control;
5772 SERVICE_DESCRIPTIONW sd = {NULL};
5773 UINT ret = ERROR_SUCCESS;
5775 comp = MSI_RecordGetString( rec, 12 );
5776 component = msi_get_loaded_component( package, comp );
5777 if (!component)
5779 WARN("service component not found\n");
5780 goto done;
5782 component->Action = msi_get_component_action( package, component );
5783 if (component->Action != INSTALLSTATE_LOCAL)
5785 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5786 goto done;
5788 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5789 if (!hscm)
5791 ERR("Failed to open the SC Manager!\n");
5792 goto done;
5795 start_type = MSI_RecordGetInteger(rec, 5);
5796 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5797 goto done;
5799 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5800 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5801 serv_type = MSI_RecordGetInteger(rec, 4);
5802 err_control = MSI_RecordGetInteger(rec, 6);
5803 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5804 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5805 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5806 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5807 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5808 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5810 /* fetch the service path */
5811 row = MSI_QueryGetRecord(package->db, query, comp);
5812 if (!row)
5814 ERR("Query failed\n");
5815 goto done;
5817 if (!(key = MSI_RecordGetString(row, 6)))
5819 msiobj_release(&row->hdr);
5820 goto done;
5822 file = msi_get_loaded_file(package, key);
5823 msiobj_release(&row->hdr);
5824 if (!file)
5826 ERR("Failed to load the service file\n");
5827 goto done;
5830 if (!args || !args[0]) image_path = file->TargetPath;
5831 else
5833 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5834 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5836 ret = ERROR_OUTOFMEMORY;
5837 goto done;
5840 strcpyW(image_path, file->TargetPath);
5841 strcatW(image_path, szSpace);
5842 strcatW(image_path, args);
5844 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5845 start_type, err_control, image_path, load_order,
5846 NULL, depends, serv_name, pass);
5848 if (!service)
5850 if (GetLastError() != ERROR_SERVICE_EXISTS)
5851 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5853 else if (sd.lpDescription)
5855 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5856 WARN("failed to set service description %u\n", GetLastError());
5859 if (image_path != file->TargetPath) msi_free(image_path);
5860 done:
5861 CloseServiceHandle(service);
5862 CloseServiceHandle(hscm);
5863 msi_free(name);
5864 msi_free(disp);
5865 msi_free(sd.lpDescription);
5866 msi_free(load_order);
5867 msi_free(serv_name);
5868 msi_free(pass);
5869 msi_free(depends);
5870 msi_free(args);
5872 return ret;
5875 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5877 static const WCHAR query[] = {
5878 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5879 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5880 MSIQUERY *view;
5881 UINT rc;
5883 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5884 if (rc != ERROR_SUCCESS)
5885 return ERROR_SUCCESS;
5887 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5888 msiobj_release(&view->hdr);
5889 return rc;
5892 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5893 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5895 LPCWSTR *vector, *temp_vector;
5896 LPWSTR p, q;
5897 DWORD sep_len;
5899 static const WCHAR separator[] = {'[','~',']',0};
5901 *numargs = 0;
5902 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5904 if (!args)
5905 return NULL;
5907 vector = msi_alloc(sizeof(LPWSTR));
5908 if (!vector)
5909 return NULL;
5911 p = args;
5914 (*numargs)++;
5915 vector[*numargs - 1] = p;
5917 if ((q = strstrW(p, separator)))
5919 *q = '\0';
5921 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5922 if (!temp_vector)
5924 msi_free(vector);
5925 return NULL;
5927 vector = temp_vector;
5929 p = q + sep_len;
5931 } while (q);
5933 return vector;
5936 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5938 MSIPACKAGE *package = param;
5939 MSICOMPONENT *comp;
5940 MSIRECORD *uirow;
5941 SC_HANDLE scm = NULL, service = NULL;
5942 LPCWSTR component, *vector = NULL;
5943 LPWSTR name, args, display_name = NULL;
5944 DWORD event, numargs, len, wait, dummy;
5945 UINT r = ERROR_FUNCTION_FAILED;
5946 SERVICE_STATUS_PROCESS status;
5947 ULONGLONG start_time;
5949 component = MSI_RecordGetString(rec, 6);
5950 comp = msi_get_loaded_component(package, component);
5951 if (!comp)
5952 return ERROR_SUCCESS;
5954 comp->Action = msi_get_component_action( package, comp );
5955 if (comp->Action != INSTALLSTATE_LOCAL)
5957 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5958 return ERROR_SUCCESS;
5961 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5962 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5963 event = MSI_RecordGetInteger(rec, 3);
5964 wait = MSI_RecordGetInteger(rec, 5);
5966 if (!(event & msidbServiceControlEventStart))
5968 r = ERROR_SUCCESS;
5969 goto done;
5972 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5973 if (!scm)
5975 ERR("Failed to open the service control manager\n");
5976 goto done;
5979 len = 0;
5980 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5981 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5983 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5984 GetServiceDisplayNameW( scm, name, display_name, &len );
5987 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5988 if (!service)
5990 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5991 goto done;
5994 vector = msi_service_args_to_vector(args, &numargs);
5996 if (!StartServiceW(service, numargs, vector) &&
5997 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5999 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6000 goto done;
6003 r = ERROR_SUCCESS;
6004 if (wait)
6006 /* wait for at most 30 seconds for the service to be up and running */
6007 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6008 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6010 TRACE("failed to query service status (%u)\n", GetLastError());
6011 goto done;
6013 start_time = GetTickCount64();
6014 while (status.dwCurrentState == SERVICE_START_PENDING)
6016 if (GetTickCount64() - start_time > 30000) break;
6017 Sleep(1000);
6018 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6019 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6021 TRACE("failed to query service status (%u)\n", GetLastError());
6022 goto done;
6025 if (status.dwCurrentState != SERVICE_RUNNING)
6027 WARN("service failed to start %u\n", status.dwCurrentState);
6028 r = ERROR_FUNCTION_FAILED;
6032 done:
6033 uirow = MSI_CreateRecord( 2 );
6034 MSI_RecordSetStringW( uirow, 1, display_name );
6035 MSI_RecordSetStringW( uirow, 2, name );
6036 msi_ui_actiondata( package, szStartServices, uirow );
6037 msiobj_release( &uirow->hdr );
6039 CloseServiceHandle(service);
6040 CloseServiceHandle(scm);
6042 msi_free(name);
6043 msi_free(args);
6044 msi_free(vector);
6045 msi_free(display_name);
6046 return r;
6049 static UINT ACTION_StartServices( MSIPACKAGE *package )
6051 static const WCHAR query[] = {
6052 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6053 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6054 MSIQUERY *view;
6055 UINT rc;
6057 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6058 if (rc != ERROR_SUCCESS)
6059 return ERROR_SUCCESS;
6061 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6062 msiobj_release(&view->hdr);
6063 return rc;
6066 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6068 DWORD i, needed, count;
6069 ENUM_SERVICE_STATUSW *dependencies;
6070 SERVICE_STATUS ss;
6071 SC_HANDLE depserv;
6072 BOOL stopped, ret = FALSE;
6074 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6075 0, &needed, &count))
6076 return TRUE;
6078 if (GetLastError() != ERROR_MORE_DATA)
6079 return FALSE;
6081 dependencies = msi_alloc(needed);
6082 if (!dependencies)
6083 return FALSE;
6085 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6086 needed, &needed, &count))
6087 goto done;
6089 for (i = 0; i < count; i++)
6091 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6092 SERVICE_STOP | SERVICE_QUERY_STATUS);
6093 if (!depserv)
6094 goto done;
6096 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6097 CloseServiceHandle(depserv);
6098 if (!stopped)
6099 goto done;
6102 ret = TRUE;
6104 done:
6105 msi_free(dependencies);
6106 return ret;
6109 static UINT stop_service( LPCWSTR name )
6111 SC_HANDLE scm = NULL, service = NULL;
6112 SERVICE_STATUS status;
6113 SERVICE_STATUS_PROCESS ssp;
6114 DWORD needed;
6116 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6117 if (!scm)
6119 WARN("Failed to open the SCM: %d\n", GetLastError());
6120 goto done;
6123 service = OpenServiceW(scm, name,
6124 SERVICE_STOP |
6125 SERVICE_QUERY_STATUS |
6126 SERVICE_ENUMERATE_DEPENDENTS);
6127 if (!service)
6129 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6130 goto done;
6133 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6134 sizeof(SERVICE_STATUS_PROCESS), &needed))
6136 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6137 goto done;
6140 if (ssp.dwCurrentState == SERVICE_STOPPED)
6141 goto done;
6143 stop_service_dependents(scm, service);
6145 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6146 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6148 done:
6149 CloseServiceHandle(service);
6150 CloseServiceHandle(scm);
6152 return ERROR_SUCCESS;
6155 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6157 MSIPACKAGE *package = param;
6158 MSICOMPONENT *comp;
6159 MSIRECORD *uirow;
6160 LPCWSTR component;
6161 LPWSTR name = NULL, display_name = NULL;
6162 DWORD event, len;
6163 SC_HANDLE scm;
6165 event = MSI_RecordGetInteger( rec, 3 );
6166 if (!(event & msidbServiceControlEventStop))
6167 return ERROR_SUCCESS;
6169 component = MSI_RecordGetString( rec, 6 );
6170 comp = msi_get_loaded_component( package, component );
6171 if (!comp)
6172 return ERROR_SUCCESS;
6174 comp->Action = msi_get_component_action( package, comp );
6175 if (comp->Action != INSTALLSTATE_ABSENT)
6177 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6178 return ERROR_SUCCESS;
6181 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6182 if (!scm)
6184 ERR("Failed to open the service control manager\n");
6185 goto done;
6188 len = 0;
6189 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6190 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6192 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6193 GetServiceDisplayNameW( scm, name, display_name, &len );
6195 CloseServiceHandle( scm );
6197 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6198 stop_service( name );
6200 done:
6201 uirow = MSI_CreateRecord( 2 );
6202 MSI_RecordSetStringW( uirow, 1, display_name );
6203 MSI_RecordSetStringW( uirow, 2, name );
6204 msi_ui_actiondata( package, szStopServices, uirow );
6205 msiobj_release( &uirow->hdr );
6207 msi_free( name );
6208 msi_free( display_name );
6209 return ERROR_SUCCESS;
6212 static UINT ACTION_StopServices( MSIPACKAGE *package )
6214 static const WCHAR query[] = {
6215 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6216 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6217 MSIQUERY *view;
6218 UINT rc;
6220 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6221 if (rc != ERROR_SUCCESS)
6222 return ERROR_SUCCESS;
6224 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6225 msiobj_release(&view->hdr);
6226 return rc;
6229 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6231 MSIPACKAGE *package = param;
6232 MSICOMPONENT *comp;
6233 MSIRECORD *uirow;
6234 LPWSTR name = NULL, display_name = NULL;
6235 DWORD event, len;
6236 SC_HANDLE scm = NULL, service = NULL;
6238 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6239 if (!comp)
6240 return ERROR_SUCCESS;
6242 event = MSI_RecordGetInteger( rec, 3 );
6243 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6245 comp->Action = msi_get_component_action( package, comp );
6246 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6247 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6249 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6250 msi_free( name );
6251 return ERROR_SUCCESS;
6253 stop_service( name );
6255 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6256 if (!scm)
6258 WARN("Failed to open the SCM: %d\n", GetLastError());
6259 goto done;
6262 len = 0;
6263 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6264 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6266 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6267 GetServiceDisplayNameW( scm, name, display_name, &len );
6270 service = OpenServiceW( scm, name, DELETE );
6271 if (!service)
6273 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6274 goto done;
6277 if (!DeleteService( service ))
6278 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6280 done:
6281 uirow = MSI_CreateRecord( 2 );
6282 MSI_RecordSetStringW( uirow, 1, display_name );
6283 MSI_RecordSetStringW( uirow, 2, name );
6284 msi_ui_actiondata( package, szDeleteServices, uirow );
6285 msiobj_release( &uirow->hdr );
6287 CloseServiceHandle( service );
6288 CloseServiceHandle( scm );
6289 msi_free( name );
6290 msi_free( display_name );
6292 return ERROR_SUCCESS;
6295 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6297 static const WCHAR query[] = {
6298 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6299 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6300 MSIQUERY *view;
6301 UINT rc;
6303 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6304 if (rc != ERROR_SUCCESS)
6305 return ERROR_SUCCESS;
6307 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6308 msiobj_release( &view->hdr );
6309 return rc;
6312 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6314 MSIPACKAGE *package = param;
6315 LPWSTR driver, driver_path, ptr;
6316 WCHAR outpath[MAX_PATH];
6317 MSIFILE *driver_file = NULL, *setup_file = NULL;
6318 MSICOMPONENT *comp;
6319 MSIRECORD *uirow;
6320 LPCWSTR desc, file_key, component;
6321 DWORD len, usage;
6322 UINT r = ERROR_SUCCESS;
6324 static const WCHAR driver_fmt[] = {
6325 'D','r','i','v','e','r','=','%','s',0};
6326 static const WCHAR setup_fmt[] = {
6327 'S','e','t','u','p','=','%','s',0};
6328 static const WCHAR usage_fmt[] = {
6329 'F','i','l','e','U','s','a','g','e','=','1',0};
6331 component = MSI_RecordGetString( rec, 2 );
6332 comp = msi_get_loaded_component( package, component );
6333 if (!comp)
6334 return ERROR_SUCCESS;
6336 comp->Action = msi_get_component_action( package, comp );
6337 if (comp->Action != INSTALLSTATE_LOCAL)
6339 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6340 return ERROR_SUCCESS;
6342 desc = MSI_RecordGetString(rec, 3);
6344 file_key = MSI_RecordGetString( rec, 4 );
6345 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6347 file_key = MSI_RecordGetString( rec, 5 );
6348 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6350 if (!driver_file)
6352 ERR("ODBC Driver entry not found!\n");
6353 return ERROR_FUNCTION_FAILED;
6356 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6357 if (setup_file)
6358 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6359 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6361 driver = msi_alloc(len * sizeof(WCHAR));
6362 if (!driver)
6363 return ERROR_OUTOFMEMORY;
6365 ptr = driver;
6366 lstrcpyW(ptr, desc);
6367 ptr += lstrlenW(ptr) + 1;
6369 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6370 ptr += len + 1;
6372 if (setup_file)
6374 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6375 ptr += len + 1;
6378 lstrcpyW(ptr, usage_fmt);
6379 ptr += lstrlenW(ptr) + 1;
6380 *ptr = '\0';
6382 if (!driver_file->TargetPath)
6384 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6385 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6387 driver_path = strdupW(driver_file->TargetPath);
6388 ptr = strrchrW(driver_path, '\\');
6389 if (ptr) *ptr = '\0';
6391 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6392 NULL, ODBC_INSTALL_COMPLETE, &usage))
6394 ERR("Failed to install SQL driver!\n");
6395 r = ERROR_FUNCTION_FAILED;
6398 uirow = MSI_CreateRecord( 5 );
6399 MSI_RecordSetStringW( uirow, 1, desc );
6400 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6401 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6402 msi_ui_actiondata( package, szInstallODBC, uirow );
6403 msiobj_release( &uirow->hdr );
6405 msi_free(driver);
6406 msi_free(driver_path);
6408 return r;
6411 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6413 MSIPACKAGE *package = param;
6414 LPWSTR translator, translator_path, ptr;
6415 WCHAR outpath[MAX_PATH];
6416 MSIFILE *translator_file = NULL, *setup_file = NULL;
6417 MSICOMPONENT *comp;
6418 MSIRECORD *uirow;
6419 LPCWSTR desc, file_key, component;
6420 DWORD len, usage;
6421 UINT r = ERROR_SUCCESS;
6423 static const WCHAR translator_fmt[] = {
6424 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6425 static const WCHAR setup_fmt[] = {
6426 'S','e','t','u','p','=','%','s',0};
6428 component = MSI_RecordGetString( rec, 2 );
6429 comp = msi_get_loaded_component( package, component );
6430 if (!comp)
6431 return ERROR_SUCCESS;
6433 comp->Action = msi_get_component_action( package, comp );
6434 if (comp->Action != INSTALLSTATE_LOCAL)
6436 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6437 return ERROR_SUCCESS;
6439 desc = MSI_RecordGetString(rec, 3);
6441 file_key = MSI_RecordGetString( rec, 4 );
6442 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6444 file_key = MSI_RecordGetString( rec, 5 );
6445 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6447 if (!translator_file)
6449 ERR("ODBC Translator entry not found!\n");
6450 return ERROR_FUNCTION_FAILED;
6453 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6454 if (setup_file)
6455 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6457 translator = msi_alloc(len * sizeof(WCHAR));
6458 if (!translator)
6459 return ERROR_OUTOFMEMORY;
6461 ptr = translator;
6462 lstrcpyW(ptr, desc);
6463 ptr += lstrlenW(ptr) + 1;
6465 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6466 ptr += len + 1;
6468 if (setup_file)
6470 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6471 ptr += len + 1;
6473 *ptr = '\0';
6475 translator_path = strdupW(translator_file->TargetPath);
6476 ptr = strrchrW(translator_path, '\\');
6477 if (ptr) *ptr = '\0';
6479 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6480 NULL, ODBC_INSTALL_COMPLETE, &usage))
6482 ERR("Failed to install SQL translator!\n");
6483 r = ERROR_FUNCTION_FAILED;
6486 uirow = MSI_CreateRecord( 5 );
6487 MSI_RecordSetStringW( uirow, 1, desc );
6488 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6489 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6490 msi_ui_actiondata( package, szInstallODBC, uirow );
6491 msiobj_release( &uirow->hdr );
6493 msi_free(translator);
6494 msi_free(translator_path);
6496 return r;
6499 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6501 MSIPACKAGE *package = param;
6502 MSICOMPONENT *comp;
6503 LPWSTR attrs;
6504 LPCWSTR desc, driver, component;
6505 WORD request = ODBC_ADD_SYS_DSN;
6506 INT registration;
6507 DWORD len;
6508 UINT r = ERROR_SUCCESS;
6509 MSIRECORD *uirow;
6511 static const WCHAR attrs_fmt[] = {
6512 'D','S','N','=','%','s',0 };
6514 component = MSI_RecordGetString( rec, 2 );
6515 comp = msi_get_loaded_component( package, component );
6516 if (!comp)
6517 return ERROR_SUCCESS;
6519 comp->Action = msi_get_component_action( package, comp );
6520 if (comp->Action != INSTALLSTATE_LOCAL)
6522 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6523 return ERROR_SUCCESS;
6526 desc = MSI_RecordGetString(rec, 3);
6527 driver = MSI_RecordGetString(rec, 4);
6528 registration = MSI_RecordGetInteger(rec, 5);
6530 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6531 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6533 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6534 attrs = msi_alloc(len * sizeof(WCHAR));
6535 if (!attrs)
6536 return ERROR_OUTOFMEMORY;
6538 len = sprintfW(attrs, attrs_fmt, desc);
6539 attrs[len + 1] = 0;
6541 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6543 ERR("Failed to install SQL data source!\n");
6544 r = ERROR_FUNCTION_FAILED;
6547 uirow = MSI_CreateRecord( 5 );
6548 MSI_RecordSetStringW( uirow, 1, desc );
6549 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6550 MSI_RecordSetInteger( uirow, 3, request );
6551 msi_ui_actiondata( package, szInstallODBC, uirow );
6552 msiobj_release( &uirow->hdr );
6554 msi_free(attrs);
6556 return r;
6559 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6561 static const WCHAR driver_query[] = {
6562 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6563 'O','D','B','C','D','r','i','v','e','r',0};
6564 static const WCHAR translator_query[] = {
6565 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6566 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6567 static const WCHAR source_query[] = {
6568 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6569 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6570 MSIQUERY *view;
6571 UINT rc;
6573 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6574 if (rc == ERROR_SUCCESS)
6576 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6577 msiobj_release(&view->hdr);
6578 if (rc != ERROR_SUCCESS)
6579 return rc;
6581 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6582 if (rc == ERROR_SUCCESS)
6584 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6585 msiobj_release(&view->hdr);
6586 if (rc != ERROR_SUCCESS)
6587 return rc;
6589 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6590 if (rc == ERROR_SUCCESS)
6592 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6593 msiobj_release(&view->hdr);
6594 if (rc != ERROR_SUCCESS)
6595 return rc;
6597 return ERROR_SUCCESS;
6600 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6602 MSIPACKAGE *package = param;
6603 MSICOMPONENT *comp;
6604 MSIRECORD *uirow;
6605 DWORD usage;
6606 LPCWSTR desc, component;
6608 component = MSI_RecordGetString( rec, 2 );
6609 comp = msi_get_loaded_component( package, component );
6610 if (!comp)
6611 return ERROR_SUCCESS;
6613 comp->Action = msi_get_component_action( package, comp );
6614 if (comp->Action != INSTALLSTATE_ABSENT)
6616 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6617 return ERROR_SUCCESS;
6620 desc = MSI_RecordGetString( rec, 3 );
6621 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6623 WARN("Failed to remove ODBC driver\n");
6625 else if (!usage)
6627 FIXME("Usage count reached 0\n");
6630 uirow = MSI_CreateRecord( 2 );
6631 MSI_RecordSetStringW( uirow, 1, desc );
6632 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6633 msi_ui_actiondata( package, szRemoveODBC, uirow );
6634 msiobj_release( &uirow->hdr );
6636 return ERROR_SUCCESS;
6639 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6641 MSIPACKAGE *package = param;
6642 MSICOMPONENT *comp;
6643 MSIRECORD *uirow;
6644 DWORD usage;
6645 LPCWSTR desc, component;
6647 component = MSI_RecordGetString( rec, 2 );
6648 comp = msi_get_loaded_component( package, component );
6649 if (!comp)
6650 return ERROR_SUCCESS;
6652 comp->Action = msi_get_component_action( package, comp );
6653 if (comp->Action != INSTALLSTATE_ABSENT)
6655 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6656 return ERROR_SUCCESS;
6659 desc = MSI_RecordGetString( rec, 3 );
6660 if (!SQLRemoveTranslatorW( desc, &usage ))
6662 WARN("Failed to remove ODBC translator\n");
6664 else if (!usage)
6666 FIXME("Usage count reached 0\n");
6669 uirow = MSI_CreateRecord( 2 );
6670 MSI_RecordSetStringW( uirow, 1, desc );
6671 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6672 msi_ui_actiondata( package, szRemoveODBC, uirow );
6673 msiobj_release( &uirow->hdr );
6675 return ERROR_SUCCESS;
6678 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6680 MSIPACKAGE *package = param;
6681 MSICOMPONENT *comp;
6682 MSIRECORD *uirow;
6683 LPWSTR attrs;
6684 LPCWSTR desc, driver, component;
6685 WORD request = ODBC_REMOVE_SYS_DSN;
6686 INT registration;
6687 DWORD len;
6689 static const WCHAR attrs_fmt[] = {
6690 'D','S','N','=','%','s',0 };
6692 component = MSI_RecordGetString( rec, 2 );
6693 comp = msi_get_loaded_component( package, component );
6694 if (!comp)
6695 return ERROR_SUCCESS;
6697 comp->Action = msi_get_component_action( package, comp );
6698 if (comp->Action != INSTALLSTATE_ABSENT)
6700 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6701 return ERROR_SUCCESS;
6704 desc = MSI_RecordGetString( rec, 3 );
6705 driver = MSI_RecordGetString( rec, 4 );
6706 registration = MSI_RecordGetInteger( rec, 5 );
6708 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6709 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6711 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6712 attrs = msi_alloc( len * sizeof(WCHAR) );
6713 if (!attrs)
6714 return ERROR_OUTOFMEMORY;
6716 FIXME("Use ODBCSourceAttribute table\n");
6718 len = sprintfW( attrs, attrs_fmt, desc );
6719 attrs[len + 1] = 0;
6721 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6723 WARN("Failed to remove ODBC data source\n");
6725 msi_free( attrs );
6727 uirow = MSI_CreateRecord( 3 );
6728 MSI_RecordSetStringW( uirow, 1, desc );
6729 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6730 MSI_RecordSetInteger( uirow, 3, request );
6731 msi_ui_actiondata( package, szRemoveODBC, uirow );
6732 msiobj_release( &uirow->hdr );
6734 return ERROR_SUCCESS;
6737 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6739 static const WCHAR driver_query[] = {
6740 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6741 'O','D','B','C','D','r','i','v','e','r',0};
6742 static const WCHAR translator_query[] = {
6743 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6744 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6745 static const WCHAR source_query[] = {
6746 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6747 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6748 MSIQUERY *view;
6749 UINT rc;
6751 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6752 if (rc == ERROR_SUCCESS)
6754 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6755 msiobj_release( &view->hdr );
6756 if (rc != ERROR_SUCCESS)
6757 return rc;
6759 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6760 if (rc == ERROR_SUCCESS)
6762 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6763 msiobj_release( &view->hdr );
6764 if (rc != ERROR_SUCCESS)
6765 return rc;
6767 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6768 if (rc == ERROR_SUCCESS)
6770 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6771 msiobj_release( &view->hdr );
6772 if (rc != ERROR_SUCCESS)
6773 return rc;
6775 return ERROR_SUCCESS;
6778 #define ENV_ACT_SETALWAYS 0x1
6779 #define ENV_ACT_SETABSENT 0x2
6780 #define ENV_ACT_REMOVE 0x4
6781 #define ENV_ACT_REMOVEMATCH 0x8
6783 #define ENV_MOD_MACHINE 0x20000000
6784 #define ENV_MOD_APPEND 0x40000000
6785 #define ENV_MOD_PREFIX 0x80000000
6786 #define ENV_MOD_MASK 0xC0000000
6788 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6790 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6792 LPCWSTR cptr = *name;
6794 static const WCHAR prefix[] = {'[','~',']',0};
6795 static const int prefix_len = 3;
6797 *flags = 0;
6798 while (*cptr)
6800 if (*cptr == '=')
6801 *flags |= ENV_ACT_SETALWAYS;
6802 else if (*cptr == '+')
6803 *flags |= ENV_ACT_SETABSENT;
6804 else if (*cptr == '-')
6805 *flags |= ENV_ACT_REMOVE;
6806 else if (*cptr == '!')
6807 *flags |= ENV_ACT_REMOVEMATCH;
6808 else if (*cptr == '*')
6809 *flags |= ENV_MOD_MACHINE;
6810 else
6811 break;
6813 cptr++;
6814 (*name)++;
6817 if (!*cptr)
6819 ERR("Missing environment variable\n");
6820 return ERROR_FUNCTION_FAILED;
6823 if (*value)
6825 LPCWSTR ptr = *value;
6826 if (!strncmpW(ptr, prefix, prefix_len))
6828 if (ptr[prefix_len] == szSemiColon[0])
6830 *flags |= ENV_MOD_APPEND;
6831 *value += lstrlenW(prefix);
6833 else
6835 *value = NULL;
6838 else if (lstrlenW(*value) >= prefix_len)
6840 ptr += lstrlenW(ptr) - prefix_len;
6841 if (!strcmpW( ptr, prefix ))
6843 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6845 *flags |= ENV_MOD_PREFIX;
6846 /* the "[~]" will be removed by deformat_string */;
6848 else
6850 *value = NULL;
6856 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6857 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6858 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6859 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6861 ERR("Invalid flags: %08x\n", *flags);
6862 return ERROR_FUNCTION_FAILED;
6865 if (!*flags)
6866 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6868 return ERROR_SUCCESS;
6871 static UINT open_env_key( DWORD flags, HKEY *key )
6873 static const WCHAR user_env[] =
6874 {'E','n','v','i','r','o','n','m','e','n','t',0};
6875 static const WCHAR machine_env[] =
6876 {'S','y','s','t','e','m','\\',
6877 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6878 'C','o','n','t','r','o','l','\\',
6879 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6880 'E','n','v','i','r','o','n','m','e','n','t',0};
6881 const WCHAR *env;
6882 HKEY root;
6883 LONG res;
6885 if (flags & ENV_MOD_MACHINE)
6887 env = machine_env;
6888 root = HKEY_LOCAL_MACHINE;
6890 else
6892 env = user_env;
6893 root = HKEY_CURRENT_USER;
6896 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6897 if (res != ERROR_SUCCESS)
6899 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6900 return ERROR_FUNCTION_FAILED;
6903 return ERROR_SUCCESS;
6906 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6908 MSIPACKAGE *package = param;
6909 LPCWSTR name, value, component;
6910 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6911 DWORD flags, type, size;
6912 UINT res;
6913 HKEY env = NULL;
6914 MSICOMPONENT *comp;
6915 MSIRECORD *uirow;
6916 int action = 0;
6918 component = MSI_RecordGetString(rec, 4);
6919 comp = msi_get_loaded_component(package, component);
6920 if (!comp)
6921 return ERROR_SUCCESS;
6923 comp->Action = msi_get_component_action( package, comp );
6924 if (comp->Action != INSTALLSTATE_LOCAL)
6926 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6927 return ERROR_SUCCESS;
6929 name = MSI_RecordGetString(rec, 2);
6930 value = MSI_RecordGetString(rec, 3);
6932 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6934 res = env_parse_flags(&name, &value, &flags);
6935 if (res != ERROR_SUCCESS || !value)
6936 goto done;
6938 if (value && !deformat_string(package, value, &deformatted))
6940 res = ERROR_OUTOFMEMORY;
6941 goto done;
6944 value = deformatted;
6946 res = open_env_key( flags, &env );
6947 if (res != ERROR_SUCCESS)
6948 goto done;
6950 if (flags & ENV_MOD_MACHINE)
6951 action |= 0x20000000;
6953 size = 0;
6954 type = REG_SZ;
6955 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6956 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6957 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6958 goto done;
6960 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6962 action = 0x2;
6964 /* Nothing to do. */
6965 if (!value)
6967 res = ERROR_SUCCESS;
6968 goto done;
6971 /* If we are appending but the string was empty, strip ; */
6972 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6974 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6975 newval = strdupW(value);
6976 if (!newval)
6978 res = ERROR_OUTOFMEMORY;
6979 goto done;
6982 else
6984 action = 0x1;
6986 /* Contrary to MSDN, +-variable to [~];path works */
6987 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6989 res = ERROR_SUCCESS;
6990 goto done;
6993 data = msi_alloc(size);
6994 if (!data)
6996 RegCloseKey(env);
6997 return ERROR_OUTOFMEMORY;
7000 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7001 if (res != ERROR_SUCCESS)
7002 goto done;
7004 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7006 action = 0x4;
7007 res = RegDeleteValueW(env, name);
7008 if (res != ERROR_SUCCESS)
7009 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7010 goto done;
7013 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7014 if (flags & ENV_MOD_MASK)
7016 DWORD mod_size;
7017 int multiplier = 0;
7018 if (flags & ENV_MOD_APPEND) multiplier++;
7019 if (flags & ENV_MOD_PREFIX) multiplier++;
7020 mod_size = lstrlenW(value) * multiplier;
7021 size += mod_size * sizeof(WCHAR);
7024 newval = msi_alloc(size);
7025 ptr = newval;
7026 if (!newval)
7028 res = ERROR_OUTOFMEMORY;
7029 goto done;
7032 if (flags & ENV_MOD_PREFIX)
7034 lstrcpyW(newval, value);
7035 ptr = newval + lstrlenW(value);
7036 action |= 0x80000000;
7039 lstrcpyW(ptr, data);
7041 if (flags & ENV_MOD_APPEND)
7043 lstrcatW(newval, value);
7044 action |= 0x40000000;
7047 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7048 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7049 if (res)
7051 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7054 done:
7055 uirow = MSI_CreateRecord( 3 );
7056 MSI_RecordSetStringW( uirow, 1, name );
7057 MSI_RecordSetStringW( uirow, 2, newval );
7058 MSI_RecordSetInteger( uirow, 3, action );
7059 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7060 msiobj_release( &uirow->hdr );
7062 if (env) RegCloseKey(env);
7063 msi_free(deformatted);
7064 msi_free(data);
7065 msi_free(newval);
7066 return res;
7069 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7071 static const WCHAR query[] = {
7072 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7073 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7074 MSIQUERY *view;
7075 UINT rc;
7077 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7078 if (rc != ERROR_SUCCESS)
7079 return ERROR_SUCCESS;
7081 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7082 msiobj_release(&view->hdr);
7083 return rc;
7086 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7088 MSIPACKAGE *package = param;
7089 LPCWSTR name, value, component;
7090 LPWSTR deformatted = NULL;
7091 DWORD flags;
7092 HKEY env;
7093 MSICOMPONENT *comp;
7094 MSIRECORD *uirow;
7095 int action = 0;
7096 LONG res;
7097 UINT r;
7099 component = MSI_RecordGetString( rec, 4 );
7100 comp = msi_get_loaded_component( package, component );
7101 if (!comp)
7102 return ERROR_SUCCESS;
7104 comp->Action = msi_get_component_action( package, comp );
7105 if (comp->Action != INSTALLSTATE_ABSENT)
7107 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7108 return ERROR_SUCCESS;
7110 name = MSI_RecordGetString( rec, 2 );
7111 value = MSI_RecordGetString( rec, 3 );
7113 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7115 r = env_parse_flags( &name, &value, &flags );
7116 if (r != ERROR_SUCCESS)
7117 return r;
7119 if (!(flags & ENV_ACT_REMOVE))
7121 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7122 return ERROR_SUCCESS;
7125 if (value && !deformat_string( package, value, &deformatted ))
7126 return ERROR_OUTOFMEMORY;
7128 value = deformatted;
7130 r = open_env_key( flags, &env );
7131 if (r != ERROR_SUCCESS)
7133 r = ERROR_SUCCESS;
7134 goto done;
7137 if (flags & ENV_MOD_MACHINE)
7138 action |= 0x20000000;
7140 TRACE("Removing %s\n", debugstr_w(name));
7142 res = RegDeleteValueW( env, name );
7143 if (res != ERROR_SUCCESS)
7145 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7146 r = ERROR_SUCCESS;
7149 done:
7150 uirow = MSI_CreateRecord( 3 );
7151 MSI_RecordSetStringW( uirow, 1, name );
7152 MSI_RecordSetStringW( uirow, 2, value );
7153 MSI_RecordSetInteger( uirow, 3, action );
7154 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7155 msiobj_release( &uirow->hdr );
7157 if (env) RegCloseKey( env );
7158 msi_free( deformatted );
7159 return r;
7162 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7164 static const WCHAR query[] = {
7165 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7166 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7167 MSIQUERY *view;
7168 UINT rc;
7170 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7171 if (rc != ERROR_SUCCESS)
7172 return ERROR_SUCCESS;
7174 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7175 msiobj_release( &view->hdr );
7176 return rc;
7179 UINT msi_validate_product_id( MSIPACKAGE *package )
7181 LPWSTR key, template, id;
7182 UINT r = ERROR_SUCCESS;
7184 id = msi_dup_property( package->db, szProductID );
7185 if (id)
7187 msi_free( id );
7188 return ERROR_SUCCESS;
7190 template = msi_dup_property( package->db, szPIDTemplate );
7191 key = msi_dup_property( package->db, szPIDKEY );
7192 if (key && template)
7194 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7195 r = msi_set_property( package->db, szProductID, key, -1 );
7197 msi_free( template );
7198 msi_free( key );
7199 return r;
7202 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7204 return msi_validate_product_id( package );
7207 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7209 TRACE("\n");
7210 package->need_reboot_at_end = 1;
7211 return ERROR_SUCCESS;
7214 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7216 static const WCHAR szAvailableFreeReg[] =
7217 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7218 MSIRECORD *uirow;
7219 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7221 TRACE("%p %d kilobytes\n", package, space);
7223 uirow = MSI_CreateRecord( 1 );
7224 MSI_RecordSetInteger( uirow, 1, space );
7225 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7226 msiobj_release( &uirow->hdr );
7228 return ERROR_SUCCESS;
7231 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7233 TRACE("%p\n", package);
7235 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7236 return ERROR_SUCCESS;
7239 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7241 FIXME("%p\n", package);
7242 return ERROR_SUCCESS;
7245 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7247 static const WCHAR driver_query[] = {
7248 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7249 'O','D','B','C','D','r','i','v','e','r',0};
7250 static const WCHAR translator_query[] = {
7251 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7252 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7253 MSIQUERY *view;
7254 UINT r, count;
7256 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7257 if (r == ERROR_SUCCESS)
7259 count = 0;
7260 r = MSI_IterateRecords( view, &count, NULL, package );
7261 msiobj_release( &view->hdr );
7262 if (r != ERROR_SUCCESS)
7263 return r;
7264 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7266 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7267 if (r == ERROR_SUCCESS)
7269 count = 0;
7270 r = MSI_IterateRecords( view, &count, NULL, package );
7271 msiobj_release( &view->hdr );
7272 if (r != ERROR_SUCCESS)
7273 return r;
7274 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7276 return ERROR_SUCCESS;
7279 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7281 static const WCHAR fmtW[] =
7282 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7283 MSIPACKAGE *package = param;
7284 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7285 int attrs = MSI_RecordGetInteger( rec, 5 );
7286 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7287 WCHAR *product, *features, *cmd;
7288 STARTUPINFOW si;
7289 PROCESS_INFORMATION info;
7290 BOOL ret;
7292 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7293 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7295 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7297 len += strlenW( product );
7298 if (features)
7299 len += strlenW( features );
7300 else
7301 len += sizeof(szAll) / sizeof(szAll[0]);
7303 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7305 msi_free( product );
7306 msi_free( features );
7307 return ERROR_OUTOFMEMORY;
7309 sprintfW( cmd, fmtW, product, features ? features : szAll );
7310 msi_free( product );
7311 msi_free( features );
7313 memset( &si, 0, sizeof(STARTUPINFOW) );
7314 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7315 msi_free( cmd );
7316 if (!ret) return GetLastError();
7317 CloseHandle( info.hThread );
7319 WaitForSingleObject( info.hProcess, INFINITE );
7320 CloseHandle( info.hProcess );
7321 return ERROR_SUCCESS;
7324 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7326 static const WCHAR query[] = {
7327 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7328 MSIQUERY *view;
7329 UINT r;
7331 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7332 if (r == ERROR_SUCCESS)
7334 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7335 msiobj_release( &view->hdr );
7336 if (r != ERROR_SUCCESS)
7337 return r;
7339 return ERROR_SUCCESS;
7342 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7344 MSIPACKAGE *package = param;
7345 int attributes = MSI_RecordGetInteger( rec, 5 );
7347 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7349 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7350 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7351 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7352 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7353 HKEY hkey;
7354 UINT r;
7356 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7358 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7359 if (r != ERROR_SUCCESS)
7360 return ERROR_SUCCESS;
7362 else
7364 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7365 if (r != ERROR_SUCCESS)
7366 return ERROR_SUCCESS;
7368 RegCloseKey( hkey );
7370 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7371 debugstr_w(upgrade_code), debugstr_w(version_min),
7372 debugstr_w(version_max), debugstr_w(language));
7374 return ERROR_SUCCESS;
7377 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7379 static const WCHAR query[] = {
7380 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7381 'U','p','g','r','a','d','e',0};
7382 MSIQUERY *view;
7383 UINT r;
7385 if (msi_get_property_int( package->db, szInstalled, 0 ))
7387 TRACE("product is installed, skipping action\n");
7388 return ERROR_SUCCESS;
7390 if (msi_get_property_int( package->db, szPreselected, 0 ))
7392 TRACE("Preselected property is set, not migrating feature states\n");
7393 return ERROR_SUCCESS;
7395 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7396 if (r == ERROR_SUCCESS)
7398 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7399 msiobj_release( &view->hdr );
7400 if (r != ERROR_SUCCESS)
7401 return r;
7403 return ERROR_SUCCESS;
7406 static void bind_image( const char *filename, const char *path )
7408 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7410 WARN("failed to bind image %u\n", GetLastError());
7414 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7416 UINT i;
7417 MSIFILE *file;
7418 MSIPACKAGE *package = param;
7419 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7420 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7421 char *filenameA, *pathA;
7422 WCHAR *pathW, **path_list;
7424 if (!(file = msi_get_loaded_file( package, key )))
7426 WARN("file %s not found\n", debugstr_w(key));
7427 return ERROR_SUCCESS;
7429 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7430 path_list = msi_split_string( paths, ';' );
7431 if (!path_list) bind_image( filenameA, NULL );
7432 else
7434 for (i = 0; path_list[i] && path_list[i][0]; i++)
7436 deformat_string( package, path_list[i], &pathW );
7437 if ((pathA = strdupWtoA( pathW )))
7439 bind_image( filenameA, pathA );
7440 msi_free( pathA );
7442 msi_free( pathW );
7445 msi_free( path_list );
7446 msi_free( filenameA );
7447 return ERROR_SUCCESS;
7450 static UINT ACTION_BindImage( MSIPACKAGE *package )
7452 static const WCHAR query[] = {
7453 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7454 'B','i','n','d','I','m','a','g','e',0};
7455 MSIQUERY *view;
7456 UINT r;
7458 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7459 if (r == ERROR_SUCCESS)
7461 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7462 msiobj_release( &view->hdr );
7463 if (r != ERROR_SUCCESS)
7464 return r;
7466 return ERROR_SUCCESS;
7469 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7471 static const WCHAR query[] = {
7472 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7473 MSIQUERY *view;
7474 DWORD count = 0;
7475 UINT r;
7477 r = MSI_OpenQuery( package->db, &view, query, table );
7478 if (r == ERROR_SUCCESS)
7480 r = MSI_IterateRecords(view, &count, NULL, package);
7481 msiobj_release(&view->hdr);
7482 if (r != ERROR_SUCCESS)
7483 return r;
7485 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7486 return ERROR_SUCCESS;
7489 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7491 static const WCHAR table[] = {
7492 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7493 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7496 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7498 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7499 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7502 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7504 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7505 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7508 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7510 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7511 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7514 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7516 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7517 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7520 static const struct
7522 const WCHAR *action;
7523 UINT (*handler)(MSIPACKAGE *);
7524 const WCHAR *action_rollback;
7526 StandardActions[] =
7528 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7529 { szAppSearch, ACTION_AppSearch, NULL },
7530 { szBindImage, ACTION_BindImage, NULL },
7531 { szCCPSearch, ACTION_CCPSearch, NULL },
7532 { szCostFinalize, ACTION_CostFinalize, NULL },
7533 { szCostInitialize, ACTION_CostInitialize, NULL },
7534 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7535 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7536 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7537 { szDisableRollback, ACTION_DisableRollback, NULL },
7538 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7539 { szExecuteAction, ACTION_ExecuteAction, NULL },
7540 { szFileCost, ACTION_FileCost, NULL },
7541 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7542 { szForceReboot, ACTION_ForceReboot, NULL },
7543 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7544 { szInstallExecute, ACTION_InstallExecute, NULL },
7545 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7546 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7547 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7548 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7549 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7550 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7551 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7552 { szInstallValidate, ACTION_InstallValidate, NULL },
7553 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7554 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7555 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7556 { szMoveFiles, ACTION_MoveFiles, NULL },
7557 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7558 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7559 { szPatchFiles, ACTION_PatchFiles, NULL },
7560 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7561 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7562 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7563 { szPublishProduct, ACTION_PublishProduct, NULL },
7564 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7565 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7566 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7567 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7568 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7569 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7570 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7571 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7572 { szRegisterUser, ACTION_RegisterUser, NULL },
7573 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7574 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7575 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7576 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7577 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7578 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7579 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7580 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7581 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7582 { szResolveSource, ACTION_ResolveSource, NULL },
7583 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7584 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7585 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7586 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7587 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7588 { szStartServices, ACTION_StartServices, szStopServices },
7589 { szStopServices, ACTION_StopServices, szStartServices },
7590 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7591 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7592 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7593 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7594 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7595 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7596 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7597 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7598 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7599 { szValidateProductID, ACTION_ValidateProductID, NULL },
7600 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7601 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7602 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7603 { NULL, NULL, NULL }
7606 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7608 BOOL ret = FALSE;
7609 UINT i;
7611 i = 0;
7612 while (StandardActions[i].action != NULL)
7614 if (!strcmpW( StandardActions[i].action, action ))
7616 ui_actionstart( package, action );
7617 if (StandardActions[i].handler)
7619 ui_actioninfo( package, action, TRUE, 0 );
7620 *rc = StandardActions[i].handler( package );
7621 ui_actioninfo( package, action, FALSE, *rc );
7623 if (StandardActions[i].action_rollback && !package->need_rollback)
7625 TRACE("scheduling rollback action\n");
7626 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7629 else
7631 FIXME("unhandled standard action %s\n", debugstr_w(action));
7632 *rc = ERROR_SUCCESS;
7634 ret = TRUE;
7635 break;
7637 i++;
7639 return ret;
7642 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7644 UINT rc = ERROR_SUCCESS;
7645 BOOL handled;
7647 TRACE("Performing action (%s)\n", debugstr_w(action));
7649 handled = ACTION_HandleStandardAction(package, action, &rc);
7651 if (!handled)
7652 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7654 if (!handled)
7656 WARN("unhandled msi action %s\n", debugstr_w(action));
7657 rc = ERROR_FUNCTION_NOT_CALLED;
7660 return rc;
7663 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7665 UINT rc = ERROR_SUCCESS;
7666 BOOL handled = FALSE;
7668 TRACE("Performing action (%s)\n", debugstr_w(action));
7670 package->action_progress_increment = 0;
7671 handled = ACTION_HandleStandardAction(package, action, &rc);
7673 if (!handled)
7674 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7676 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7677 handled = TRUE;
7679 if (!handled)
7681 WARN("unhandled msi action %s\n", debugstr_w(action));
7682 rc = ERROR_FUNCTION_NOT_CALLED;
7685 return rc;
7688 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7690 UINT rc = ERROR_SUCCESS;
7691 MSIRECORD *row;
7693 static const WCHAR query[] =
7694 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7695 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7696 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7697 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7698 static const WCHAR ui_query[] =
7699 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7700 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7701 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7702 ' ', '=',' ','%','i',0};
7704 if (needs_ui_sequence(package))
7705 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7706 else
7707 row = MSI_QueryGetRecord(package->db, query, seq);
7709 if (row)
7711 LPCWSTR action, cond;
7713 TRACE("Running the actions\n");
7715 /* check conditions */
7716 cond = MSI_RecordGetString(row, 2);
7718 /* this is a hack to skip errors in the condition code */
7719 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7721 msiobj_release(&row->hdr);
7722 return ERROR_SUCCESS;
7725 action = MSI_RecordGetString(row, 1);
7726 if (!action)
7728 ERR("failed to fetch action\n");
7729 msiobj_release(&row->hdr);
7730 return ERROR_FUNCTION_FAILED;
7733 if (needs_ui_sequence(package))
7734 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7735 else
7736 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7738 msiobj_release(&row->hdr);
7741 return rc;
7744 /****************************************************
7745 * TOP level entry points
7746 *****************************************************/
7748 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7749 LPCWSTR szCommandLine )
7751 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7752 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7753 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7754 WCHAR *reinstall, *remove, *patch;
7755 BOOL ui_exists;
7756 UINT rc;
7758 msi_set_property( package->db, szAction, szInstall, -1 );
7760 package->script->InWhatSequence = SEQUENCE_INSTALL;
7762 if (szPackagePath)
7764 LPWSTR p, dir;
7765 LPCWSTR file;
7767 dir = strdupW(szPackagePath);
7768 p = strrchrW(dir, '\\');
7769 if (p)
7771 *(++p) = 0;
7772 file = szPackagePath + (p - dir);
7774 else
7776 msi_free(dir);
7777 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7778 GetCurrentDirectoryW(MAX_PATH, dir);
7779 lstrcatW(dir, szBackSlash);
7780 file = szPackagePath;
7783 msi_free( package->PackagePath );
7784 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7785 if (!package->PackagePath)
7787 msi_free(dir);
7788 return ERROR_OUTOFMEMORY;
7791 lstrcpyW(package->PackagePath, dir);
7792 lstrcatW(package->PackagePath, file);
7793 msi_free(dir);
7795 msi_set_sourcedir_props(package, FALSE);
7798 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7799 if (rc != ERROR_SUCCESS)
7800 return rc;
7802 msi_apply_transforms( package );
7803 msi_apply_patches( package );
7805 patch = msi_dup_property( package->db, szPatch );
7806 remove = msi_dup_property( package->db, szRemove );
7807 reinstall = msi_dup_property( package->db, szReinstall );
7808 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7810 TRACE("setting REINSTALL property to ALL\n");
7811 msi_set_property( package->db, szReinstall, szAll, -1 );
7812 package->full_reinstall = 1;
7815 /* properties may have been added by a transform */
7816 msi_clone_properties( package );
7817 msi_set_original_database_property( package->db, szPackagePath );
7819 msi_parse_command_line( package, szCommandLine, FALSE );
7820 msi_adjust_privilege_properties( package );
7821 msi_set_context( package );
7823 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7825 TRACE("disabling rollback\n");
7826 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7829 if (needs_ui_sequence( package))
7831 package->script->InWhatSequence |= SEQUENCE_UI;
7832 rc = ACTION_ProcessUISequence(package);
7833 ui_exists = ui_sequence_exists(package);
7834 if (rc == ERROR_SUCCESS || !ui_exists)
7836 package->script->InWhatSequence |= SEQUENCE_EXEC;
7837 rc = ACTION_ProcessExecSequence(package, ui_exists);
7840 else
7841 rc = ACTION_ProcessExecSequence(package, FALSE);
7843 /* process the ending type action */
7844 if (rc == ERROR_SUCCESS)
7845 ACTION_PerformActionSequence(package, -1);
7846 else if (rc == ERROR_INSTALL_USEREXIT)
7847 ACTION_PerformActionSequence(package, -2);
7848 else if (rc == ERROR_INSTALL_SUSPEND)
7849 ACTION_PerformActionSequence(package, -4);
7850 else /* failed */
7852 ACTION_PerformActionSequence(package, -3);
7853 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7855 package->need_rollback = TRUE;
7859 /* finish up running custom actions */
7860 ACTION_FinishCustomActions(package);
7862 if (package->need_rollback && !reinstall)
7864 WARN("installation failed, running rollback script\n");
7865 execute_script( package, SCRIPT_ROLLBACK );
7867 msi_free( reinstall );
7868 msi_free( remove );
7869 msi_free( patch );
7871 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7872 return ERROR_SUCCESS_REBOOT_REQUIRED;
7874 return rc;