msi: Remove the unused CurrentlyScripting field from MSISCRIPT.
[wine/wine-gecko.git] / dlls / msi / action.c
blob73aae3569b835dc951ad31189765bacb37cafda4
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;
1670 override = msi_dup_property( package->db, property );
1671 if (!override)
1672 return FALSE;
1674 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1676 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1677 continue;
1679 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1681 if (!strcmpiW( override, szAll ))
1683 if (feature->Installed != state)
1685 feature->Action = state;
1686 feature->ActionRequest = state;
1689 else
1691 LPWSTR ptr = override;
1692 LPWSTR ptr2 = strchrW(override,',');
1694 while (ptr)
1696 int len = ptr2 - ptr;
1698 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1699 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1701 if (feature->Installed != state)
1703 feature->Action = state;
1704 feature->ActionRequest = state;
1706 break;
1708 if (ptr2)
1710 ptr=ptr2+1;
1711 ptr2 = strchrW(ptr,',');
1713 else
1714 break;
1718 msi_free(override);
1719 return TRUE;
1722 static BOOL process_overrides( MSIPACKAGE *package, int level )
1724 static const WCHAR szAddLocal[] =
1725 {'A','D','D','L','O','C','A','L',0};
1726 static const WCHAR szAddSource[] =
1727 {'A','D','D','S','O','U','R','C','E',0};
1728 static const WCHAR szAdvertise[] =
1729 {'A','D','V','E','R','T','I','S','E',0};
1730 BOOL ret = FALSE;
1732 /* all these activation/deactivation things happen in order and things
1733 * later on the list override things earlier on the list.
1735 * 0 INSTALLLEVEL processing
1736 * 1 ADDLOCAL
1737 * 2 REMOVE
1738 * 3 ADDSOURCE
1739 * 4 ADDDEFAULT
1740 * 5 REINSTALL
1741 * 6 ADVERTISE
1742 * 7 COMPADDLOCAL
1743 * 8 COMPADDSOURCE
1744 * 9 FILEADDLOCAL
1745 * 10 FILEADDSOURCE
1746 * 11 FILEADDDEFAULT
1748 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1749 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1750 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1751 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1752 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1754 if (ret && !package->full_reinstall)
1755 msi_set_property( package->db, szPreselected, szOne, -1 );
1757 return ret;
1760 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1762 int level;
1763 MSICOMPONENT* component;
1764 MSIFEATURE *feature;
1766 TRACE("Checking Install Level\n");
1768 level = msi_get_property_int(package->db, szInstallLevel, 1);
1770 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1772 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1774 if (!is_feature_selected( feature, level )) continue;
1776 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1778 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1780 feature->Action = INSTALLSTATE_SOURCE;
1781 feature->ActionRequest = INSTALLSTATE_SOURCE;
1783 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1785 feature->Action = INSTALLSTATE_ADVERTISED;
1786 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1788 else
1790 feature->Action = INSTALLSTATE_LOCAL;
1791 feature->ActionRequest = INSTALLSTATE_LOCAL;
1795 /* disable child features of unselected parent or follow parent */
1796 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1798 FeatureList *fl;
1800 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1802 if (!is_feature_selected( feature, level ))
1804 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1805 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1807 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1809 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1810 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1811 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1812 fl->feature->Action = feature->Action;
1813 fl->feature->ActionRequest = feature->ActionRequest;
1818 else /* preselected */
1820 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1822 if (!is_feature_selected( feature, level )) continue;
1824 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1826 if (feature->Installed == INSTALLSTATE_ABSENT)
1828 feature->Action = INSTALLSTATE_UNKNOWN;
1829 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1831 else
1833 feature->Action = feature->Installed;
1834 feature->ActionRequest = feature->Installed;
1838 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1840 FeatureList *fl;
1842 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1844 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1845 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1847 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1848 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1849 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1850 fl->feature->Action = feature->Action;
1851 fl->feature->ActionRequest = feature->ActionRequest;
1857 /* now we want to set component state based based on feature state */
1858 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1860 ComponentList *cl;
1862 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1863 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1864 feature->ActionRequest, feature->Action);
1866 if (!is_feature_selected( feature, level )) continue;
1868 /* features with components that have compressed files are made local */
1869 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1871 if (cl->component->ForceLocalState &&
1872 feature->ActionRequest == INSTALLSTATE_SOURCE)
1874 feature->Action = INSTALLSTATE_LOCAL;
1875 feature->ActionRequest = INSTALLSTATE_LOCAL;
1876 break;
1880 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1882 component = cl->component;
1884 switch (feature->ActionRequest)
1886 case INSTALLSTATE_ABSENT:
1887 component->anyAbsent = 1;
1888 break;
1889 case INSTALLSTATE_ADVERTISED:
1890 component->hasAdvertiseFeature = 1;
1891 break;
1892 case INSTALLSTATE_SOURCE:
1893 component->hasSourceFeature = 1;
1894 break;
1895 case INSTALLSTATE_LOCAL:
1896 component->hasLocalFeature = 1;
1897 break;
1898 case INSTALLSTATE_DEFAULT:
1899 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1900 component->hasAdvertiseFeature = 1;
1901 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1902 component->hasSourceFeature = 1;
1903 else
1904 component->hasLocalFeature = 1;
1905 break;
1906 default:
1907 break;
1912 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1914 /* check if it's local or source */
1915 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1916 (component->hasLocalFeature || component->hasSourceFeature))
1918 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1919 !component->ForceLocalState)
1921 component->Action = INSTALLSTATE_SOURCE;
1922 component->ActionRequest = INSTALLSTATE_SOURCE;
1924 else
1926 component->Action = INSTALLSTATE_LOCAL;
1927 component->ActionRequest = INSTALLSTATE_LOCAL;
1929 continue;
1932 /* if any feature is local, the component must be local too */
1933 if (component->hasLocalFeature)
1935 component->Action = INSTALLSTATE_LOCAL;
1936 component->ActionRequest = INSTALLSTATE_LOCAL;
1937 continue;
1939 if (component->hasSourceFeature)
1941 component->Action = INSTALLSTATE_SOURCE;
1942 component->ActionRequest = INSTALLSTATE_SOURCE;
1943 continue;
1945 if (component->hasAdvertiseFeature)
1947 component->Action = INSTALLSTATE_ADVERTISED;
1948 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1949 continue;
1951 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1952 if (component->anyAbsent && component->ComponentId)
1954 component->Action = INSTALLSTATE_ABSENT;
1955 component->ActionRequest = INSTALLSTATE_ABSENT;
1959 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1961 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1963 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1964 component->Action = INSTALLSTATE_LOCAL;
1965 component->ActionRequest = INSTALLSTATE_LOCAL;
1968 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1969 component->Installed == INSTALLSTATE_SOURCE &&
1970 component->hasSourceFeature)
1972 component->Action = INSTALLSTATE_UNKNOWN;
1973 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1976 TRACE("component %s (installed %d request %d action %d)\n",
1977 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1979 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
1980 component->num_clients++;
1981 else if (component->Action == INSTALLSTATE_ABSENT)
1982 component->num_clients--;
1985 return ERROR_SUCCESS;
1988 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1990 MSIPACKAGE *package = param;
1991 LPCWSTR name;
1992 MSIFEATURE *feature;
1994 name = MSI_RecordGetString( row, 1 );
1996 feature = msi_get_loaded_feature( package, name );
1997 if (!feature)
1998 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1999 else
2001 LPCWSTR Condition;
2002 Condition = MSI_RecordGetString(row,3);
2004 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2006 int level = MSI_RecordGetInteger(row,2);
2007 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2008 feature->Level = level;
2011 return ERROR_SUCCESS;
2014 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2016 static const WCHAR name[] = {'\\',0};
2017 VS_FIXEDFILEINFO *ptr, *ret;
2018 LPVOID version;
2019 DWORD versize, handle;
2020 UINT sz;
2022 versize = GetFileVersionInfoSizeW( filename, &handle );
2023 if (!versize)
2024 return NULL;
2026 version = msi_alloc( versize );
2027 if (!version)
2028 return NULL;
2030 GetFileVersionInfoW( filename, 0, versize, version );
2032 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2034 msi_free( version );
2035 return NULL;
2038 ret = msi_alloc( sz );
2039 memcpy( ret, ptr, sz );
2041 msi_free( version );
2042 return ret;
2045 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2047 DWORD ms, ls;
2049 msi_parse_version_string( version, &ms, &ls );
2051 if (fi->dwFileVersionMS > ms) return 1;
2052 else if (fi->dwFileVersionMS < ms) return -1;
2053 else if (fi->dwFileVersionLS > ls) return 1;
2054 else if (fi->dwFileVersionLS < ls) return -1;
2055 return 0;
2058 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2060 DWORD ms1, ms2;
2062 msi_parse_version_string( ver1, &ms1, NULL );
2063 msi_parse_version_string( ver2, &ms2, NULL );
2065 if (ms1 > ms2) return 1;
2066 else if (ms1 < ms2) return -1;
2067 return 0;
2070 DWORD msi_get_disk_file_size( LPCWSTR filename )
2072 HANDLE file;
2073 DWORD size;
2075 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2076 if (file == INVALID_HANDLE_VALUE)
2077 return INVALID_FILE_SIZE;
2079 size = GetFileSize( file, NULL );
2080 TRACE("size is %u\n", size);
2081 CloseHandle( file );
2082 return size;
2085 BOOL msi_file_hash_matches( MSIFILE *file )
2087 UINT r;
2088 MSIFILEHASHINFO hash;
2090 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2091 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2092 if (r != ERROR_SUCCESS)
2093 return FALSE;
2095 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2098 static WCHAR *get_temp_dir( void )
2100 static UINT id;
2101 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2103 GetTempPathW( MAX_PATH, tmp );
2104 for (;;)
2106 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2107 if (CreateDirectoryW( dir, NULL )) break;
2109 return strdupW( dir );
2113 * msi_build_directory_name()
2115 * This function is to save messing round with directory names
2116 * It handles adding backslashes between path segments,
2117 * and can add \ at the end of the directory name if told to.
2119 * It takes a variable number of arguments.
2120 * It always allocates a new string for the result, so make sure
2121 * to free the return value when finished with it.
2123 * The first arg is the number of path segments that follow.
2124 * The arguments following count are a list of path segments.
2125 * A path segment may be NULL.
2127 * Path segments will be added with a \ separating them.
2128 * A \ will not be added after the last segment, however if the
2129 * last segment is NULL, then the last character will be a \
2131 WCHAR *msi_build_directory_name( DWORD count, ... )
2133 DWORD sz = 1, i;
2134 WCHAR *dir;
2135 va_list va;
2137 va_start( va, count );
2138 for (i = 0; i < count; i++)
2140 const WCHAR *str = va_arg( va, const WCHAR * );
2141 if (str) sz += strlenW( str ) + 1;
2143 va_end( va );
2145 dir = msi_alloc( sz * sizeof(WCHAR) );
2146 dir[0] = 0;
2148 va_start( va, count );
2149 for (i = 0; i < count; i++)
2151 const WCHAR *str = va_arg( va, const WCHAR * );
2152 if (!str) continue;
2153 strcatW( dir, str );
2154 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2156 va_end( va );
2157 return dir;
2160 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2162 MSIASSEMBLY *assembly = file->Component->assembly;
2164 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2166 msi_free( file->TargetPath );
2167 if (assembly && !assembly->application)
2169 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2170 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2171 msi_track_tempfile( package, file->TargetPath );
2173 else
2175 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2176 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2179 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2182 static UINT calculate_file_cost( MSIPACKAGE *package )
2184 VS_FIXEDFILEINFO *file_version;
2185 WCHAR *font_version;
2186 MSIFILE *file;
2188 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2190 MSICOMPONENT *comp = file->Component;
2191 DWORD file_size;
2193 if (!comp->Enabled) continue;
2195 if (file->IsCompressed)
2196 comp->ForceLocalState = TRUE;
2198 set_target_path( package, file );
2200 if ((comp->assembly && !comp->assembly->installed) ||
2201 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2203 comp->Cost += file->FileSize;
2204 continue;
2206 file_size = msi_get_disk_file_size( file->TargetPath );
2208 if (file->Version)
2210 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2212 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2214 comp->Cost += file->FileSize - file_size;
2216 msi_free( file_version );
2217 continue;
2219 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2221 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2223 comp->Cost += file->FileSize - file_size;
2225 msi_free( font_version );
2226 continue;
2229 if (file_size != file->FileSize)
2231 comp->Cost += file->FileSize - file_size;
2234 return ERROR_SUCCESS;
2237 WCHAR *msi_normalize_path( const WCHAR *in )
2239 const WCHAR *p = in;
2240 WCHAR *q, *ret;
2241 int n, len = strlenW( in ) + 2;
2243 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2245 len = 0;
2246 while (1)
2248 /* copy until the end of the string or a space */
2249 while (*p != ' ' && (*q = *p))
2251 p++, len++;
2252 /* reduce many backslashes to one */
2253 if (*p != '\\' || *q != '\\')
2254 q++;
2257 /* quit at the end of the string */
2258 if (!*p)
2259 break;
2261 /* count the number of spaces */
2262 n = 0;
2263 while (p[n] == ' ')
2264 n++;
2266 /* if it's leading or trailing space, skip it */
2267 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2268 p += n;
2269 else /* copy n spaces */
2270 while (n && (*q++ = *p++)) n--;
2272 while (q - ret > 0 && q[-1] == ' ') q--;
2273 if (q - ret > 0 && q[-1] != '\\')
2275 q[0] = '\\';
2276 q[1] = 0;
2278 return ret;
2281 static WCHAR *get_install_location( MSIPACKAGE *package )
2283 HKEY hkey;
2284 WCHAR *path;
2286 if (!package->ProductCode) return NULL;
2287 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2288 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2290 msi_free( path );
2291 path = NULL;
2293 RegCloseKey( hkey );
2294 return path;
2297 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2299 FolderList *fl;
2300 MSIFOLDER *folder, *parent, *child;
2301 WCHAR *path, *normalized_path;
2303 TRACE("resolving %s\n", debugstr_w(name));
2305 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2307 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2309 if (!(path = get_install_location( package )) &&
2310 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2312 path = msi_dup_property( package->db, szRootDrive );
2315 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2317 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2319 parent = msi_get_loaded_folder( package, folder->Parent );
2320 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2322 else
2323 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2325 normalized_path = msi_normalize_path( path );
2326 msi_free( path );
2327 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2329 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2330 msi_free( normalized_path );
2331 return;
2333 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2334 msi_free( folder->ResolvedTarget );
2335 folder->ResolvedTarget = normalized_path;
2337 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2339 child = fl->folder;
2340 msi_resolve_target_folder( package, child->Directory, load_prop );
2342 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2345 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2347 static const WCHAR query[] =
2348 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2349 '`','C','o','n','d','i','t','i','o','n','`',0};
2350 static const WCHAR szOutOfDiskSpace[] =
2351 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2352 static const WCHAR szPrimaryFolder[] =
2353 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2354 static const WCHAR szPrimaryVolumePath[] =
2355 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2356 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2357 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2358 'A','v','a','i','l','a','b','l','e',0};
2359 MSICOMPONENT *comp;
2360 MSIQUERY *view;
2361 WCHAR *level, *primary_key, *primary_folder;
2362 UINT rc;
2364 TRACE("Building directory properties\n");
2365 msi_resolve_target_folder( package, szTargetDir, TRUE );
2367 TRACE("Evaluating component conditions\n");
2368 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2370 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2372 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2373 comp->Enabled = FALSE;
2375 else
2376 comp->Enabled = TRUE;
2378 get_client_counts( package );
2380 /* read components states from the registry */
2381 ACTION_GetComponentInstallStates(package);
2382 ACTION_GetFeatureInstallStates(package);
2384 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2386 TRACE("Evaluating feature conditions\n");
2388 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2389 if (rc == ERROR_SUCCESS)
2391 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2392 msiobj_release( &view->hdr );
2393 if (rc != ERROR_SUCCESS)
2394 return rc;
2398 TRACE("Calculating file cost\n");
2399 calculate_file_cost( package );
2401 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2402 /* set default run level if not set */
2403 level = msi_dup_property( package->db, szInstallLevel );
2404 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2405 msi_free(level);
2407 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2409 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2411 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2412 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2414 ULARGE_INTEGER free;
2416 primary_folder[2] = 0;
2417 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2419 static const WCHAR fmtW[] = {'%','l','u',0};
2420 WCHAR buf[21];
2422 sprintfW( buf, fmtW, free.QuadPart / 512 );
2423 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2425 toupperW( primary_folder[0] );
2426 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2428 msi_free( primary_folder );
2430 msi_free( primary_key );
2433 /* FIXME: check volume disk space */
2434 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2436 return MSI_SetFeatureStates(package);
2439 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size )
2441 BYTE *data = NULL;
2443 if (!value)
2445 *size = sizeof(WCHAR);
2446 *type = REG_SZ;
2447 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2448 return data;
2450 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2452 if (value[1]=='x')
2454 LPWSTR ptr;
2455 CHAR byte[5];
2456 LPWSTR deformated = NULL;
2457 int count;
2459 deformat_string(package, &value[2], &deformated);
2461 /* binary value type */
2462 ptr = deformated;
2463 *type = REG_BINARY;
2464 if (strlenW(ptr)%2)
2465 *size = (strlenW(ptr)/2)+1;
2466 else
2467 *size = strlenW(ptr)/2;
2469 data = msi_alloc(*size);
2471 byte[0] = '0';
2472 byte[1] = 'x';
2473 byte[4] = 0;
2474 count = 0;
2475 /* if uneven pad with a zero in front */
2476 if (strlenW(ptr)%2)
2478 byte[2]= '0';
2479 byte[3]= *ptr;
2480 ptr++;
2481 data[count] = (BYTE)strtol(byte,NULL,0);
2482 count ++;
2483 TRACE("Uneven byte count\n");
2485 while (*ptr)
2487 byte[2]= *ptr;
2488 ptr++;
2489 byte[3]= *ptr;
2490 ptr++;
2491 data[count] = (BYTE)strtol(byte,NULL,0);
2492 count ++;
2494 msi_free(deformated);
2496 TRACE("Data %i bytes(%i)\n",*size,count);
2498 else
2500 LPWSTR deformated;
2501 LPWSTR p;
2502 DWORD d = 0;
2503 deformat_string(package, &value[1], &deformated);
2505 *type=REG_DWORD;
2506 *size = sizeof(DWORD);
2507 data = msi_alloc(*size);
2508 p = deformated;
2509 if (*p == '-')
2510 p++;
2511 while (*p)
2513 if ( (*p < '0') || (*p > '9') )
2514 break;
2515 d *= 10;
2516 d += (*p - '0');
2517 p++;
2519 if (deformated[0] == '-')
2520 d = -d;
2521 *(LPDWORD)data = d;
2522 TRACE("DWORD %i\n",*(LPDWORD)data);
2524 msi_free(deformated);
2527 else
2529 const WCHAR *ptr = value;
2530 DWORD len;
2532 *type = REG_SZ;
2533 if (value[0] == '#')
2535 ptr++;
2536 if (value[1] == '%')
2538 ptr++;
2539 *type = REG_EXPAND_SZ;
2542 len = deformat_string( package, ptr, (WCHAR **)&data );
2543 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2544 *size = (len + 1) * sizeof(WCHAR);
2546 return data;
2549 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2551 const WCHAR *ret;
2553 switch (root)
2555 case -1:
2556 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2558 *root_key = HKEY_LOCAL_MACHINE;
2559 ret = szHLM;
2561 else
2563 *root_key = HKEY_CURRENT_USER;
2564 ret = szHCU;
2566 break;
2567 case 0:
2568 *root_key = HKEY_CLASSES_ROOT;
2569 ret = szHCR;
2570 break;
2571 case 1:
2572 *root_key = HKEY_CURRENT_USER;
2573 ret = szHCU;
2574 break;
2575 case 2:
2576 *root_key = HKEY_LOCAL_MACHINE;
2577 ret = szHLM;
2578 break;
2579 case 3:
2580 *root_key = HKEY_USERS;
2581 ret = szHU;
2582 break;
2583 default:
2584 ERR("Unknown root %i\n", root);
2585 return NULL;
2588 return ret;
2591 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2593 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2594 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2596 if ((is_64bit || is_wow64) &&
2597 !(comp->Attributes & msidbComponentAttributes64bit) &&
2598 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2600 UINT size;
2601 WCHAR *path_32node;
2603 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2604 if (!(path_32node = msi_alloc( size ))) return NULL;
2606 memcpy( path_32node, path, len * sizeof(WCHAR) );
2607 strcpyW( path_32node + len, szWow6432Node );
2608 strcatW( path_32node, szBackSlash );
2609 strcatW( path_32node, path + len );
2610 return path_32node;
2612 return strdupW( path );
2615 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2617 REGSAM access = KEY_ALL_ACCESS;
2618 WCHAR *subkey, *p, *q;
2619 HKEY hkey, ret = NULL;
2620 LONG res;
2622 if (is_wow64) access |= KEY_WOW64_64KEY;
2624 if (!(subkey = strdupW( path ))) return NULL;
2625 p = subkey;
2626 if ((q = strchrW( p, '\\' ))) *q = 0;
2627 if (create)
2628 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2629 else
2630 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2631 if (res)
2633 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2634 msi_free( subkey );
2635 return NULL;
2637 if (q && q[1])
2639 ret = open_key( hkey, q + 1, create );
2640 RegCloseKey( hkey );
2642 else ret = hkey;
2643 msi_free( subkey );
2644 return ret;
2647 static BOOL is_special_entry( const WCHAR *name )
2649 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2652 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2654 const WCHAR *p = str;
2655 WCHAR **ret;
2656 int i = 0;
2658 *count = 0;
2659 if (!str) return NULL;
2660 while ((p - str) < len)
2662 p += strlenW( p ) + 1;
2663 (*count)++;
2665 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2666 p = str;
2667 while ((p - str) < len)
2669 if (!(ret[i] = strdupW( p )))
2671 for (; i >= 0; i--) msi_free( ret[i] );
2672 msi_free( ret );
2673 return NULL;
2675 p += strlenW( p ) + 1;
2676 i++;
2678 return ret;
2681 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2682 WCHAR **right, DWORD right_count, DWORD *size )
2684 WCHAR *ret, *p;
2685 unsigned int i;
2687 *size = sizeof(WCHAR);
2688 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2689 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2691 if (!(ret = p = msi_alloc( *size ))) return NULL;
2693 for (i = 0; i < left_count; i++)
2695 strcpyW( p, left[i] );
2696 p += strlenW( p ) + 1;
2698 for (i = 0; i < right_count; i++)
2700 strcpyW( p, right[i] );
2701 p += strlenW( p ) + 1;
2703 *p = 0;
2704 return ret;
2707 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2708 WCHAR **new, DWORD new_count )
2710 DWORD ret = old_count;
2711 unsigned int i, j, k;
2713 for (i = 0; i < new_count; i++)
2715 for (j = 0; j < old_count; j++)
2717 if (old[j] && !strcmpW( new[i], old[j] ))
2719 msi_free( old[j] );
2720 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2721 old[k] = NULL;
2722 ret--;
2726 return ret;
2729 enum join_op
2731 JOIN_OP_APPEND,
2732 JOIN_OP_PREPEND,
2733 JOIN_OP_REPLACE
2736 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2737 WCHAR **new, DWORD new_count, DWORD *size )
2739 switch (op)
2741 case JOIN_OP_APPEND:
2742 old_count = remove_duplicate_values( old, old_count, new, new_count );
2743 return flatten_multi_string_values( old, old_count, new, new_count, size );
2745 case JOIN_OP_PREPEND:
2746 old_count = remove_duplicate_values( old, old_count, new, new_count );
2747 return flatten_multi_string_values( new, new_count, old, old_count, size );
2749 case JOIN_OP_REPLACE:
2750 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2752 default:
2753 ERR("unhandled join op %u\n", op);
2754 return NULL;
2758 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2759 BYTE *new_value, DWORD new_size, DWORD *size )
2761 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2762 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2763 enum join_op op = JOIN_OP_REPLACE;
2764 WCHAR **old = NULL, **new = NULL;
2765 BYTE *ret;
2767 if (new_size / sizeof(WCHAR) - 1 > 1)
2769 new_ptr = (const WCHAR *)new_value;
2770 new_len = new_size / sizeof(WCHAR) - 1;
2772 if (!new_ptr[0] && new_ptr[new_len - 1])
2774 op = JOIN_OP_APPEND;
2775 new_len--;
2776 new_ptr++;
2778 else if (new_ptr[0] && !new_ptr[new_len - 1])
2780 op = JOIN_OP_PREPEND;
2781 new_len--;
2783 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2785 op = JOIN_OP_REPLACE;
2786 new_len -= 2;
2787 new_ptr++;
2789 new = split_multi_string_values( new_ptr, new_len, &new_count );
2791 if (old_size / sizeof(WCHAR) - 1 > 1)
2793 old_ptr = (const WCHAR *)old_value;
2794 old_len = old_size / sizeof(WCHAR) - 1;
2795 old = split_multi_string_values( old_ptr, old_len, &old_count );
2797 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2798 for (i = 0; i < old_count; i++) msi_free( old[i] );
2799 for (i = 0; i < new_count; i++) msi_free( new[i] );
2800 msi_free( old );
2801 msi_free( new );
2802 return ret;
2805 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2807 BYTE *ret;
2808 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2809 if (!(ret = msi_alloc( *size ))) return NULL;
2810 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2811 return ret;
2814 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2816 MSIPACKAGE *package = param;
2817 BYTE *new_value, *old_value = NULL;
2818 HKEY root_key, hkey;
2819 DWORD type, old_type, new_size, old_size = 0;
2820 LPWSTR deformated, uikey, keypath;
2821 const WCHAR *szRoot, *component, *name, *key, *str;
2822 MSICOMPONENT *comp;
2823 MSIRECORD * uirow;
2824 INT root;
2825 BOOL check_first = FALSE;
2826 int len;
2828 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2830 component = MSI_RecordGetString(row, 6);
2831 comp = msi_get_loaded_component(package,component);
2832 if (!comp)
2833 return ERROR_SUCCESS;
2835 comp->Action = msi_get_component_action( package, comp );
2836 if (comp->Action != INSTALLSTATE_LOCAL)
2838 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2839 return ERROR_SUCCESS;
2842 name = MSI_RecordGetString(row, 4);
2843 if( MSI_RecordIsNull(row,5) && name )
2845 /* null values can have special meanings */
2846 if (name[0]=='-' && name[1] == 0)
2847 return ERROR_SUCCESS;
2848 if ((name[0] == '+' || name[0] == '*') && !name[1])
2849 check_first = TRUE;
2852 root = MSI_RecordGetInteger(row,2);
2853 key = MSI_RecordGetString(row, 3);
2855 szRoot = get_root_key( package, root, &root_key );
2856 if (!szRoot)
2857 return ERROR_SUCCESS;
2859 deformat_string(package, key , &deformated);
2860 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2861 strcpyW(uikey,szRoot);
2862 strcatW(uikey,deformated);
2864 keypath = get_keypath( comp, root_key, deformated );
2865 msi_free( deformated );
2866 if (!(hkey = open_key( root_key, keypath, TRUE )))
2868 ERR("Could not create key %s\n", debugstr_w(keypath));
2869 msi_free(uikey);
2870 msi_free(keypath);
2871 return ERROR_FUNCTION_FAILED;
2873 str = msi_record_get_string( row, 5, &len );
2874 if (str && len > strlenW( str ))
2876 type = REG_MULTI_SZ;
2877 new_size = (len + 1) * sizeof(WCHAR);
2878 new_value = (BYTE *)msi_strdupW( str, len );
2880 else new_value = parse_value( package, str, &type, &new_size );
2881 deformat_string(package, name, &deformated);
2883 if (!is_special_entry( name ))
2885 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2886 if (type == REG_MULTI_SZ)
2888 BYTE *new;
2889 if (old_value && old_type != REG_MULTI_SZ)
2891 msi_free( old_value );
2892 old_value = NULL;
2893 old_size = 0;
2895 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2896 msi_free( new_value );
2897 new_value = new;
2899 if (!check_first)
2901 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2902 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2904 else if (!old_value)
2906 if (deformated || new_size)
2908 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2909 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2912 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2914 RegCloseKey(hkey);
2916 uirow = MSI_CreateRecord(3);
2917 MSI_RecordSetStringW(uirow,2,deformated);
2918 MSI_RecordSetStringW(uirow,1,uikey);
2919 if (type == REG_SZ || type == REG_EXPAND_SZ)
2920 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2921 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2922 msiobj_release( &uirow->hdr );
2924 msi_free(new_value);
2925 msi_free(old_value);
2926 msi_free(deformated);
2927 msi_free(uikey);
2928 msi_free(keypath);
2930 return ERROR_SUCCESS;
2933 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2935 static const WCHAR query[] = {
2936 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2937 '`','R','e','g','i','s','t','r','y','`',0};
2938 MSIQUERY *view;
2939 UINT rc;
2941 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2942 if (rc != ERROR_SUCCESS)
2943 return ERROR_SUCCESS;
2945 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2946 msiobj_release(&view->hdr);
2947 return rc;
2950 static void delete_key( HKEY root, const WCHAR *path )
2952 REGSAM access = 0;
2953 WCHAR *subkey, *p;
2954 HKEY hkey;
2955 LONG res;
2957 if (is_wow64) access |= KEY_WOW64_64KEY;
2959 if (!(subkey = strdupW( path ))) return;
2960 for (;;)
2962 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2963 hkey = open_key( root, subkey, FALSE );
2964 if (!hkey) break;
2965 if (p && p[1])
2966 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2967 else
2968 res = RegDeleteKeyExW( root, subkey, access, 0 );
2969 if (res)
2971 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2972 break;
2974 if (p && p[1]) RegCloseKey( hkey );
2975 else break;
2977 msi_free( subkey );
2980 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2982 LONG res;
2983 HKEY hkey;
2984 DWORD num_subkeys, num_values;
2986 if ((hkey = open_key( root, path, FALSE )))
2988 if ((res = RegDeleteValueW( hkey, value )))
2989 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2991 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2992 NULL, NULL, NULL, NULL );
2993 RegCloseKey( hkey );
2994 if (!res && !num_subkeys && !num_values)
2996 TRACE("removing empty key %s\n", debugstr_w(path));
2997 delete_key( root, path );
3002 static void delete_tree( HKEY root, const WCHAR *path )
3004 LONG res;
3005 HKEY hkey;
3007 if (!(hkey = open_key( root, path, FALSE ))) return;
3008 res = RegDeleteTreeW( hkey, NULL );
3009 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3010 delete_key( root, path );
3011 RegCloseKey( hkey );
3014 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3016 MSIPACKAGE *package = param;
3017 LPCWSTR component, name, key_str, root_key_str;
3018 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3019 MSICOMPONENT *comp;
3020 MSIRECORD *uirow;
3021 BOOL delete_key = FALSE;
3022 HKEY hkey_root;
3023 UINT size;
3024 INT root;
3026 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3028 component = MSI_RecordGetString( row, 6 );
3029 comp = msi_get_loaded_component( package, component );
3030 if (!comp)
3031 return ERROR_SUCCESS;
3033 comp->Action = msi_get_component_action( package, comp );
3034 if (comp->Action != INSTALLSTATE_ABSENT)
3036 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3037 return ERROR_SUCCESS;
3040 name = MSI_RecordGetString( row, 4 );
3041 if (MSI_RecordIsNull( row, 5 ) && name )
3043 if (name[0] == '+' && !name[1])
3044 return ERROR_SUCCESS;
3045 if ((name[0] == '-' || name[0] == '*') && !name[1])
3047 delete_key = TRUE;
3048 name = NULL;
3052 root = MSI_RecordGetInteger( row, 2 );
3053 key_str = MSI_RecordGetString( row, 3 );
3055 root_key_str = get_root_key( package, root, &hkey_root );
3056 if (!root_key_str)
3057 return ERROR_SUCCESS;
3059 deformat_string( package, key_str, &deformated_key );
3060 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3061 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3062 strcpyW( ui_key_str, root_key_str );
3063 strcatW( ui_key_str, deformated_key );
3065 deformat_string( package, name, &deformated_name );
3067 keypath = get_keypath( comp, hkey_root, deformated_key );
3068 msi_free( deformated_key );
3069 if (delete_key) delete_tree( hkey_root, keypath );
3070 else delete_value( hkey_root, keypath, deformated_name );
3071 msi_free( keypath );
3073 uirow = MSI_CreateRecord( 2 );
3074 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3075 MSI_RecordSetStringW( uirow, 2, deformated_name );
3076 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3077 msiobj_release( &uirow->hdr );
3079 msi_free( ui_key_str );
3080 msi_free( deformated_name );
3081 return ERROR_SUCCESS;
3084 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3086 MSIPACKAGE *package = param;
3087 LPCWSTR component, name, key_str, root_key_str;
3088 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3089 MSICOMPONENT *comp;
3090 MSIRECORD *uirow;
3091 BOOL delete_key = FALSE;
3092 HKEY hkey_root;
3093 UINT size;
3094 INT root;
3096 component = MSI_RecordGetString( row, 5 );
3097 comp = msi_get_loaded_component( package, component );
3098 if (!comp)
3099 return ERROR_SUCCESS;
3101 comp->Action = msi_get_component_action( package, comp );
3102 if (comp->Action != INSTALLSTATE_LOCAL)
3104 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3105 return ERROR_SUCCESS;
3108 if ((name = MSI_RecordGetString( row, 4 )))
3110 if (name[0] == '-' && !name[1])
3112 delete_key = TRUE;
3113 name = NULL;
3117 root = MSI_RecordGetInteger( row, 2 );
3118 key_str = MSI_RecordGetString( row, 3 );
3120 root_key_str = get_root_key( package, root, &hkey_root );
3121 if (!root_key_str)
3122 return ERROR_SUCCESS;
3124 deformat_string( package, key_str, &deformated_key );
3125 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3126 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3127 strcpyW( ui_key_str, root_key_str );
3128 strcatW( ui_key_str, deformated_key );
3130 deformat_string( package, name, &deformated_name );
3132 keypath = get_keypath( comp, hkey_root, deformated_key );
3133 msi_free( deformated_key );
3134 if (delete_key) delete_tree( hkey_root, keypath );
3135 else delete_value( hkey_root, keypath, deformated_name );
3136 msi_free( keypath );
3138 uirow = MSI_CreateRecord( 2 );
3139 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3140 MSI_RecordSetStringW( uirow, 2, deformated_name );
3141 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3142 msiobj_release( &uirow->hdr );
3144 msi_free( ui_key_str );
3145 msi_free( deformated_name );
3146 return ERROR_SUCCESS;
3149 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3151 static const WCHAR registry_query[] = {
3152 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3153 '`','R','e','g','i','s','t','r','y','`',0};
3154 static const WCHAR remove_registry_query[] = {
3155 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3156 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3157 MSIQUERY *view;
3158 UINT rc;
3160 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3161 if (rc == ERROR_SUCCESS)
3163 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3164 msiobj_release( &view->hdr );
3165 if (rc != ERROR_SUCCESS)
3166 return rc;
3168 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3169 if (rc == ERROR_SUCCESS)
3171 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3172 msiobj_release( &view->hdr );
3173 if (rc != ERROR_SUCCESS)
3174 return rc;
3176 return ERROR_SUCCESS;
3179 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3181 return ERROR_SUCCESS;
3185 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3187 static const WCHAR query[]= {
3188 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3189 '`','R','e','g','i','s','t','r','y','`',0};
3190 MSICOMPONENT *comp;
3191 DWORD total = 0, count = 0;
3192 MSIQUERY *view;
3193 MSIFEATURE *feature;
3194 MSIFILE *file;
3195 UINT rc;
3197 TRACE("InstallValidate\n");
3199 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3200 if (rc == ERROR_SUCCESS)
3202 rc = MSI_IterateRecords( view, &count, NULL, package );
3203 msiobj_release( &view->hdr );
3204 if (rc != ERROR_SUCCESS)
3205 return rc;
3206 total += count * REG_PROGRESS_VALUE;
3208 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3209 total += COMPONENT_PROGRESS_VALUE;
3211 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3212 total += file->FileSize;
3214 msi_ui_progress( package, 0, total, 0, 0 );
3216 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3218 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3219 debugstr_w(feature->Feature), feature->Installed,
3220 feature->ActionRequest, feature->Action);
3222 return ERROR_SUCCESS;
3225 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3227 MSIPACKAGE* package = param;
3228 LPCWSTR cond = NULL;
3229 LPCWSTR message = NULL;
3230 UINT r;
3232 static const WCHAR title[]=
3233 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3235 cond = MSI_RecordGetString(row,1);
3237 r = MSI_EvaluateConditionW(package,cond);
3238 if (r == MSICONDITION_FALSE)
3240 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3242 LPWSTR deformated;
3243 message = MSI_RecordGetString(row,2);
3244 deformat_string(package,message,&deformated);
3245 MessageBoxW(NULL,deformated,title,MB_OK);
3246 msi_free(deformated);
3249 return ERROR_INSTALL_FAILURE;
3252 return ERROR_SUCCESS;
3255 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3257 static const WCHAR query[] = {
3258 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3259 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3260 MSIQUERY *view;
3261 UINT rc;
3263 TRACE("Checking launch conditions\n");
3265 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3266 if (rc != ERROR_SUCCESS)
3267 return ERROR_SUCCESS;
3269 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3270 msiobj_release(&view->hdr);
3271 return rc;
3274 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3277 if (!cmp->KeyPath)
3278 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3280 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3282 static const WCHAR query[] = {
3283 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3284 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3285 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3286 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3287 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3288 MSIRECORD *row;
3289 UINT root, len;
3290 LPWSTR deformated, buffer, deformated_name;
3291 LPCWSTR key, name;
3293 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3294 if (!row)
3295 return NULL;
3297 root = MSI_RecordGetInteger(row,2);
3298 key = MSI_RecordGetString(row, 3);
3299 name = MSI_RecordGetString(row, 4);
3300 deformat_string(package, key , &deformated);
3301 deformat_string(package, name, &deformated_name);
3303 len = strlenW(deformated) + 6;
3304 if (deformated_name)
3305 len+=strlenW(deformated_name);
3307 buffer = msi_alloc( len *sizeof(WCHAR));
3309 if (deformated_name)
3310 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3311 else
3312 sprintfW(buffer,fmt,root,deformated);
3314 msi_free(deformated);
3315 msi_free(deformated_name);
3316 msiobj_release(&row->hdr);
3318 return buffer;
3320 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3322 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3323 return NULL;
3325 else
3327 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3329 if (file)
3330 return strdupW( file->TargetPath );
3332 return NULL;
3335 static HKEY openSharedDLLsKey(void)
3337 HKEY hkey=0;
3338 static const WCHAR path[] =
3339 {'S','o','f','t','w','a','r','e','\\',
3340 'M','i','c','r','o','s','o','f','t','\\',
3341 'W','i','n','d','o','w','s','\\',
3342 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3343 'S','h','a','r','e','d','D','L','L','s',0};
3345 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3346 return hkey;
3349 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3351 HKEY hkey;
3352 DWORD count=0;
3353 DWORD type;
3354 DWORD sz = sizeof(count);
3355 DWORD rc;
3357 hkey = openSharedDLLsKey();
3358 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3359 if (rc != ERROR_SUCCESS)
3360 count = 0;
3361 RegCloseKey(hkey);
3362 return count;
3365 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3367 HKEY hkey;
3369 hkey = openSharedDLLsKey();
3370 if (count > 0)
3371 msi_reg_set_val_dword( hkey, path, count );
3372 else
3373 RegDeleteValueW(hkey,path);
3374 RegCloseKey(hkey);
3375 return count;
3378 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3380 MSIFEATURE *feature;
3381 INT count = 0;
3382 BOOL write = FALSE;
3384 /* only refcount DLLs */
3385 if (comp->KeyPath == NULL ||
3386 comp->assembly ||
3387 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3388 comp->Attributes & msidbComponentAttributesODBCDataSource)
3389 write = FALSE;
3390 else
3392 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3393 write = (count > 0);
3395 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3396 write = TRUE;
3399 /* increment counts */
3400 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3402 ComponentList *cl;
3404 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3405 continue;
3407 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3409 if ( cl->component == comp )
3410 count++;
3414 /* decrement counts */
3415 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3417 ComponentList *cl;
3419 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3420 continue;
3422 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3424 if ( cl->component == comp )
3425 count--;
3429 /* ref count all the files in the component */
3430 if (write)
3432 MSIFILE *file;
3434 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3436 if (file->Component == comp)
3437 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3441 /* add a count for permanent */
3442 if (comp->Attributes & msidbComponentAttributesPermanent)
3443 count ++;
3445 comp->RefCount = count;
3447 if (write)
3448 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3451 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3453 if (comp->assembly)
3455 const WCHAR prefixW[] = {'<','\\',0};
3456 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3457 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3459 if (keypath)
3461 strcpyW( keypath, prefixW );
3462 strcatW( keypath, comp->assembly->display_name );
3464 return keypath;
3466 return resolve_keypath( package, comp );
3469 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3471 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3472 UINT rc;
3473 MSICOMPONENT *comp;
3474 HKEY hkey;
3476 TRACE("\n");
3478 squash_guid(package->ProductCode,squished_pc);
3479 msi_set_sourcedir_props(package, FALSE);
3481 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3483 MSIRECORD *uirow;
3484 INSTALLSTATE action;
3486 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3487 if (!comp->ComponentId)
3488 continue;
3490 squash_guid( comp->ComponentId, squished_cc );
3491 msi_free( comp->FullKeypath );
3492 comp->FullKeypath = build_full_keypath( package, comp );
3494 ACTION_RefCountComponent( package, comp );
3496 if (package->need_rollback) action = comp->Installed;
3497 else action = comp->ActionRequest;
3499 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3500 debugstr_w(comp->Component), debugstr_w(squished_cc),
3501 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3503 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3505 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3506 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3507 else
3508 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3510 if (rc != ERROR_SUCCESS)
3511 continue;
3513 if (comp->Attributes & msidbComponentAttributesPermanent)
3515 static const WCHAR szPermKey[] =
3516 { '0','0','0','0','0','0','0','0','0','0','0','0',
3517 '0','0','0','0','0','0','0','0','0','0','0','0',
3518 '0','0','0','0','0','0','0','0',0 };
3520 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3522 if (action == INSTALLSTATE_LOCAL)
3523 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3524 else
3526 MSIFILE *file;
3527 MSIRECORD *row;
3528 LPWSTR ptr, ptr2;
3529 WCHAR source[MAX_PATH];
3530 WCHAR base[MAX_PATH];
3531 LPWSTR sourcepath;
3533 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3534 static const WCHAR query[] = {
3535 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3536 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3537 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3538 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3539 '`','D','i','s','k','I','d','`',0};
3541 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3542 continue;
3544 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3545 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3546 ptr2 = strrchrW(source, '\\') + 1;
3547 msiobj_release(&row->hdr);
3549 lstrcpyW(base, package->PackagePath);
3550 ptr = strrchrW(base, '\\');
3551 *(ptr + 1) = '\0';
3553 sourcepath = msi_resolve_file_source(package, file);
3554 ptr = sourcepath + lstrlenW(base);
3555 lstrcpyW(ptr2, ptr);
3556 msi_free(sourcepath);
3558 msi_reg_set_val_str(hkey, squished_pc, source);
3560 RegCloseKey(hkey);
3562 else if (action == INSTALLSTATE_ABSENT)
3564 if (comp->num_clients <= 0)
3566 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3567 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3568 else
3569 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3573 /* UI stuff */
3574 uirow = MSI_CreateRecord(3);
3575 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3576 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3577 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3578 msi_ui_actiondata( package, szProcessComponents, uirow );
3579 msiobj_release( &uirow->hdr );
3581 return ERROR_SUCCESS;
3584 typedef struct {
3585 CLSID clsid;
3586 LPWSTR source;
3588 LPWSTR path;
3589 ITypeLib *ptLib;
3590 } typelib_struct;
3592 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3593 LPWSTR lpszName, LONG_PTR lParam)
3595 TLIBATTR *attr;
3596 typelib_struct *tl_struct = (typelib_struct*) lParam;
3597 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3598 int sz;
3599 HRESULT res;
3601 if (!IS_INTRESOURCE(lpszName))
3603 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3604 return TRUE;
3607 sz = strlenW(tl_struct->source)+4;
3608 sz *= sizeof(WCHAR);
3610 if ((INT_PTR)lpszName == 1)
3611 tl_struct->path = strdupW(tl_struct->source);
3612 else
3614 tl_struct->path = msi_alloc(sz);
3615 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3618 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3619 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3620 if (FAILED(res))
3622 msi_free(tl_struct->path);
3623 tl_struct->path = NULL;
3625 return TRUE;
3628 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3629 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3631 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3632 return FALSE;
3635 msi_free(tl_struct->path);
3636 tl_struct->path = NULL;
3638 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3639 ITypeLib_Release(tl_struct->ptLib);
3641 return TRUE;
3644 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3646 MSIPACKAGE* package = param;
3647 LPCWSTR component;
3648 MSICOMPONENT *comp;
3649 MSIFILE *file;
3650 typelib_struct tl_struct;
3651 ITypeLib *tlib;
3652 HMODULE module;
3653 HRESULT hr;
3655 component = MSI_RecordGetString(row,3);
3656 comp = msi_get_loaded_component(package,component);
3657 if (!comp)
3658 return ERROR_SUCCESS;
3660 comp->Action = msi_get_component_action( package, comp );
3661 if (comp->Action != INSTALLSTATE_LOCAL)
3663 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3664 return ERROR_SUCCESS;
3667 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3669 TRACE("component has no key path\n");
3670 return ERROR_SUCCESS;
3672 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3674 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3675 if (module)
3677 LPCWSTR guid;
3678 guid = MSI_RecordGetString(row,1);
3679 CLSIDFromString( guid, &tl_struct.clsid);
3680 tl_struct.source = strdupW( file->TargetPath );
3681 tl_struct.path = NULL;
3683 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3684 (LONG_PTR)&tl_struct);
3686 if (tl_struct.path)
3688 LPCWSTR helpid, help_path = NULL;
3689 HRESULT res;
3691 helpid = MSI_RecordGetString(row,6);
3693 if (helpid) help_path = msi_get_target_folder( package, helpid );
3694 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3696 if (FAILED(res))
3697 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3698 else
3699 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3701 ITypeLib_Release(tl_struct.ptLib);
3702 msi_free(tl_struct.path);
3704 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3706 FreeLibrary(module);
3707 msi_free(tl_struct.source);
3709 else
3711 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3712 if (FAILED(hr))
3714 ERR("Failed to load type library: %08x\n", hr);
3715 return ERROR_INSTALL_FAILURE;
3718 ITypeLib_Release(tlib);
3721 return ERROR_SUCCESS;
3724 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3726 static const WCHAR query[] = {
3727 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3728 '`','T','y','p','e','L','i','b','`',0};
3729 MSIQUERY *view;
3730 UINT rc;
3732 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3733 if (rc != ERROR_SUCCESS)
3734 return ERROR_SUCCESS;
3736 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3737 msiobj_release(&view->hdr);
3738 return rc;
3741 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3743 MSIPACKAGE *package = param;
3744 LPCWSTR component, guid;
3745 MSICOMPONENT *comp;
3746 GUID libid;
3747 UINT version;
3748 LCID language;
3749 SYSKIND syskind;
3750 HRESULT hr;
3752 component = MSI_RecordGetString( row, 3 );
3753 comp = msi_get_loaded_component( package, component );
3754 if (!comp)
3755 return ERROR_SUCCESS;
3757 comp->Action = msi_get_component_action( package, comp );
3758 if (comp->Action != INSTALLSTATE_ABSENT)
3760 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3761 return ERROR_SUCCESS;
3763 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3765 guid = MSI_RecordGetString( row, 1 );
3766 CLSIDFromString( guid, &libid );
3767 version = MSI_RecordGetInteger( row, 4 );
3768 language = MSI_RecordGetInteger( row, 2 );
3770 #ifdef _WIN64
3771 syskind = SYS_WIN64;
3772 #else
3773 syskind = SYS_WIN32;
3774 #endif
3776 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3777 if (FAILED(hr))
3779 WARN("Failed to unregister typelib: %08x\n", hr);
3782 return ERROR_SUCCESS;
3785 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3787 static const WCHAR query[] = {
3788 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3789 '`','T','y','p','e','L','i','b','`',0};
3790 MSIQUERY *view;
3791 UINT rc;
3793 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3794 if (rc != ERROR_SUCCESS)
3795 return ERROR_SUCCESS;
3797 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3798 msiobj_release( &view->hdr );
3799 return rc;
3802 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3804 static const WCHAR szlnk[] = {'.','l','n','k',0};
3805 LPCWSTR directory, extension, link_folder;
3806 LPWSTR link_file, filename;
3808 directory = MSI_RecordGetString( row, 2 );
3809 link_folder = msi_get_target_folder( package, directory );
3810 if (!link_folder)
3812 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3813 return NULL;
3815 /* may be needed because of a bug somewhere else */
3816 msi_create_full_path( link_folder );
3818 filename = msi_dup_record_field( row, 3 );
3819 msi_reduce_to_long_filename( filename );
3821 extension = strrchrW( filename, '.' );
3822 if (!extension || strcmpiW( extension, szlnk ))
3824 int len = strlenW( filename );
3825 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3826 memcpy( filename + len, szlnk, sizeof(szlnk) );
3828 link_file = msi_build_directory_name( 2, link_folder, filename );
3829 msi_free( filename );
3831 return link_file;
3834 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3836 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3837 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3838 WCHAR *folder, *dest, *path;
3840 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3841 folder = msi_dup_property( package->db, szWindowsFolder );
3842 else
3844 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3845 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3846 msi_free( appdata );
3848 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3849 msi_create_full_path( dest );
3850 path = msi_build_directory_name( 2, dest, icon_name );
3851 msi_free( folder );
3852 msi_free( dest );
3853 return path;
3856 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3858 MSIPACKAGE *package = param;
3859 LPWSTR link_file, deformated, path;
3860 LPCWSTR component, target;
3861 MSICOMPONENT *comp;
3862 IShellLinkW *sl = NULL;
3863 IPersistFile *pf = NULL;
3864 HRESULT res;
3866 component = MSI_RecordGetString(row, 4);
3867 comp = msi_get_loaded_component(package, component);
3868 if (!comp)
3869 return ERROR_SUCCESS;
3871 comp->Action = msi_get_component_action( package, comp );
3872 if (comp->Action != INSTALLSTATE_LOCAL)
3874 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3875 return ERROR_SUCCESS;
3877 msi_ui_actiondata( package, szCreateShortcuts, row );
3879 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3880 &IID_IShellLinkW, (LPVOID *) &sl );
3882 if (FAILED( res ))
3884 ERR("CLSID_ShellLink not available\n");
3885 goto err;
3888 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3889 if (FAILED( res ))
3891 ERR("QueryInterface(IID_IPersistFile) failed\n");
3892 goto err;
3895 target = MSI_RecordGetString(row, 5);
3896 if (strchrW(target, '['))
3898 deformat_string( package, target, &path );
3899 TRACE("target path is %s\n", debugstr_w(path));
3900 IShellLinkW_SetPath( sl, path );
3901 msi_free( path );
3903 else
3905 FIXME("poorly handled shortcut format, advertised shortcut\n");
3906 IShellLinkW_SetPath(sl,comp->FullKeypath);
3909 if (!MSI_RecordIsNull(row,6))
3911 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3912 deformat_string(package, arguments, &deformated);
3913 IShellLinkW_SetArguments(sl,deformated);
3914 msi_free(deformated);
3917 if (!MSI_RecordIsNull(row,7))
3919 LPCWSTR description = MSI_RecordGetString(row, 7);
3920 IShellLinkW_SetDescription(sl, description);
3923 if (!MSI_RecordIsNull(row,8))
3924 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3926 if (!MSI_RecordIsNull(row,9))
3928 INT index;
3929 LPCWSTR icon = MSI_RecordGetString(row, 9);
3931 path = msi_build_icon_path(package, icon);
3932 index = MSI_RecordGetInteger(row,10);
3934 /* no value means 0 */
3935 if (index == MSI_NULL_INTEGER)
3936 index = 0;
3938 IShellLinkW_SetIconLocation(sl, path, index);
3939 msi_free(path);
3942 if (!MSI_RecordIsNull(row,11))
3943 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3945 if (!MSI_RecordIsNull(row,12))
3947 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3948 full_path = msi_get_target_folder( package, wkdir );
3949 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3951 link_file = get_link_file(package, row);
3953 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3954 IPersistFile_Save(pf, link_file, FALSE);
3955 msi_free(link_file);
3957 err:
3958 if (pf)
3959 IPersistFile_Release( pf );
3960 if (sl)
3961 IShellLinkW_Release( sl );
3963 return ERROR_SUCCESS;
3966 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3968 static const WCHAR query[] = {
3969 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3970 '`','S','h','o','r','t','c','u','t','`',0};
3971 MSIQUERY *view;
3972 HRESULT res;
3973 UINT rc;
3975 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3976 if (rc != ERROR_SUCCESS)
3977 return ERROR_SUCCESS;
3979 res = CoInitialize( NULL );
3981 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3982 msiobj_release(&view->hdr);
3984 if (SUCCEEDED(res)) CoUninitialize();
3985 return rc;
3988 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3990 MSIPACKAGE *package = param;
3991 LPWSTR link_file;
3992 LPCWSTR component;
3993 MSICOMPONENT *comp;
3995 component = MSI_RecordGetString( row, 4 );
3996 comp = msi_get_loaded_component( package, component );
3997 if (!comp)
3998 return ERROR_SUCCESS;
4000 comp->Action = msi_get_component_action( package, comp );
4001 if (comp->Action != INSTALLSTATE_ABSENT)
4003 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4004 return ERROR_SUCCESS;
4006 msi_ui_actiondata( package, szRemoveShortcuts, row );
4008 link_file = get_link_file( package, row );
4010 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4011 if (!DeleteFileW( link_file ))
4013 WARN("Failed to remove shortcut file %u\n", GetLastError());
4015 msi_free( link_file );
4017 return ERROR_SUCCESS;
4020 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4022 static const WCHAR query[] = {
4023 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4024 '`','S','h','o','r','t','c','u','t','`',0};
4025 MSIQUERY *view;
4026 UINT rc;
4028 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4029 if (rc != ERROR_SUCCESS)
4030 return ERROR_SUCCESS;
4032 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4033 msiobj_release( &view->hdr );
4034 return rc;
4037 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4039 MSIPACKAGE* package = param;
4040 HANDLE the_file;
4041 LPWSTR FilePath;
4042 LPCWSTR FileName;
4043 CHAR buffer[1024];
4044 DWORD sz;
4045 UINT rc;
4047 FileName = MSI_RecordGetString(row,1);
4048 if (!FileName)
4050 ERR("Unable to get FileName\n");
4051 return ERROR_SUCCESS;
4054 FilePath = msi_build_icon_path(package, FileName);
4056 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4058 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4059 FILE_ATTRIBUTE_NORMAL, NULL);
4061 if (the_file == INVALID_HANDLE_VALUE)
4063 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4064 msi_free(FilePath);
4065 return ERROR_SUCCESS;
4070 DWORD write;
4071 sz = 1024;
4072 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4073 if (rc != ERROR_SUCCESS)
4075 ERR("Failed to get stream\n");
4076 CloseHandle(the_file);
4077 DeleteFileW(FilePath);
4078 break;
4080 WriteFile(the_file,buffer,sz,&write,NULL);
4081 } while (sz == 1024);
4083 msi_free(FilePath);
4084 CloseHandle(the_file);
4086 return ERROR_SUCCESS;
4089 static UINT msi_publish_icons(MSIPACKAGE *package)
4091 static const WCHAR query[]= {
4092 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4093 '`','I','c','o','n','`',0};
4094 MSIQUERY *view;
4095 UINT r;
4097 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4098 if (r == ERROR_SUCCESS)
4100 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4101 msiobj_release(&view->hdr);
4102 if (r != ERROR_SUCCESS)
4103 return r;
4105 return ERROR_SUCCESS;
4108 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4110 UINT r;
4111 HKEY source;
4112 LPWSTR buffer;
4113 MSIMEDIADISK *disk;
4114 MSISOURCELISTINFO *info;
4116 r = RegCreateKeyW(hkey, szSourceList, &source);
4117 if (r != ERROR_SUCCESS)
4118 return r;
4120 RegCloseKey(source);
4122 buffer = strrchrW(package->PackagePath, '\\') + 1;
4123 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4124 package->Context, MSICODE_PRODUCT,
4125 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4126 if (r != ERROR_SUCCESS)
4127 return r;
4129 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4130 package->Context, MSICODE_PRODUCT,
4131 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4132 if (r != ERROR_SUCCESS)
4133 return r;
4135 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4136 package->Context, MSICODE_PRODUCT,
4137 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4138 if (r != ERROR_SUCCESS)
4139 return r;
4141 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4143 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4144 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4145 info->options, info->value);
4146 else
4147 MsiSourceListSetInfoW(package->ProductCode, NULL,
4148 info->context, info->options,
4149 info->property, info->value);
4152 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4154 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4155 disk->context, disk->options,
4156 disk->disk_id, disk->volume_label, disk->disk_prompt);
4159 return ERROR_SUCCESS;
4162 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4164 MSIHANDLE hdb, suminfo;
4165 WCHAR guids[MAX_PATH];
4166 WCHAR packcode[SQUISH_GUID_SIZE];
4167 LPWSTR buffer;
4168 LPWSTR ptr;
4169 DWORD langid;
4170 DWORD size;
4171 UINT r;
4173 static const WCHAR szARPProductIcon[] =
4174 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4175 static const WCHAR szAssignment[] =
4176 {'A','s','s','i','g','n','m','e','n','t',0};
4177 static const WCHAR szAdvertiseFlags[] =
4178 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4179 static const WCHAR szClients[] =
4180 {'C','l','i','e','n','t','s',0};
4181 static const WCHAR szColon[] = {':',0};
4183 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4184 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4185 msi_free(buffer);
4187 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4188 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4190 /* FIXME */
4191 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4193 buffer = msi_dup_property(package->db, szARPProductIcon);
4194 if (buffer)
4196 LPWSTR path = msi_build_icon_path(package, buffer);
4197 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4198 msi_free(path);
4199 msi_free(buffer);
4202 buffer = msi_dup_property(package->db, szProductVersion);
4203 if (buffer)
4205 DWORD verdword = msi_version_str_to_dword(buffer);
4206 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4207 msi_free(buffer);
4210 msi_reg_set_val_dword(hkey, szAssignment, 0);
4211 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4212 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4213 msi_reg_set_val_str(hkey, szClients, szColon);
4215 hdb = alloc_msihandle(&package->db->hdr);
4216 if (!hdb)
4217 return ERROR_NOT_ENOUGH_MEMORY;
4219 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4220 MsiCloseHandle(hdb);
4221 if (r != ERROR_SUCCESS)
4222 goto done;
4224 size = MAX_PATH;
4225 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4226 NULL, guids, &size);
4227 if (r != ERROR_SUCCESS)
4228 goto done;
4230 ptr = strchrW(guids, ';');
4231 if (ptr) *ptr = 0;
4232 squash_guid(guids, packcode);
4233 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4235 done:
4236 MsiCloseHandle(suminfo);
4237 return ERROR_SUCCESS;
4240 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4242 UINT r;
4243 HKEY hkey;
4244 LPWSTR upgrade;
4245 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4247 upgrade = msi_dup_property(package->db, szUpgradeCode);
4248 if (!upgrade)
4249 return ERROR_SUCCESS;
4251 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4252 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4253 else
4254 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4256 if (r != ERROR_SUCCESS)
4258 WARN("failed to open upgrade code key\n");
4259 msi_free(upgrade);
4260 return ERROR_SUCCESS;
4262 squash_guid(package->ProductCode, squashed_pc);
4263 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4264 RegCloseKey(hkey);
4265 msi_free(upgrade);
4266 return ERROR_SUCCESS;
4269 static BOOL msi_check_publish(MSIPACKAGE *package)
4271 MSIFEATURE *feature;
4273 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4275 feature->Action = msi_get_feature_action( package, feature );
4276 if (feature->Action == INSTALLSTATE_LOCAL)
4277 return TRUE;
4280 return FALSE;
4283 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4285 MSIFEATURE *feature;
4287 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4289 feature->Action = msi_get_feature_action( package, feature );
4290 if (feature->Action != INSTALLSTATE_ABSENT)
4291 return FALSE;
4294 return TRUE;
4297 static UINT msi_publish_patches( MSIPACKAGE *package )
4299 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4300 WCHAR patch_squashed[GUID_SIZE];
4301 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4302 LONG res;
4303 MSIPATCHINFO *patch;
4304 UINT r;
4305 WCHAR *p, *all_patches = NULL;
4306 DWORD len = 0;
4308 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4309 if (r != ERROR_SUCCESS)
4310 return ERROR_FUNCTION_FAILED;
4312 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4313 if (res != ERROR_SUCCESS)
4315 r = ERROR_FUNCTION_FAILED;
4316 goto done;
4319 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4320 if (r != ERROR_SUCCESS)
4321 goto done;
4323 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4325 squash_guid( patch->patchcode, patch_squashed );
4326 len += strlenW( patch_squashed ) + 1;
4329 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4330 if (!all_patches)
4331 goto done;
4333 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4335 HKEY patch_key;
4337 squash_guid( patch->patchcode, p );
4338 p += strlenW( p ) + 1;
4340 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4341 (const BYTE *)patch->transforms,
4342 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4343 if (res != ERROR_SUCCESS)
4344 goto done;
4346 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4347 if (r != ERROR_SUCCESS)
4348 goto done;
4350 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4351 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4352 RegCloseKey( patch_key );
4353 if (res != ERROR_SUCCESS)
4354 goto done;
4356 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4358 res = GetLastError();
4359 ERR("Unable to copy patch package %d\n", res);
4360 goto done;
4362 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4363 if (res != ERROR_SUCCESS)
4364 goto done;
4366 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4367 RegCloseKey( patch_key );
4368 if (res != ERROR_SUCCESS)
4369 goto done;
4372 all_patches[len] = 0;
4373 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4374 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4375 if (res != ERROR_SUCCESS)
4376 goto done;
4378 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4379 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4380 if (res != ERROR_SUCCESS)
4381 r = ERROR_FUNCTION_FAILED;
4383 done:
4384 RegCloseKey( product_patches_key );
4385 RegCloseKey( patches_key );
4386 RegCloseKey( product_key );
4387 msi_free( all_patches );
4388 return r;
4391 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4393 UINT rc;
4394 HKEY hukey = NULL, hudkey = NULL;
4395 MSIRECORD *uirow;
4397 if (!list_empty(&package->patches))
4399 rc = msi_publish_patches(package);
4400 if (rc != ERROR_SUCCESS)
4401 goto end;
4404 /* FIXME: also need to publish if the product is in advertise mode */
4405 if (!msi_check_publish(package))
4406 return ERROR_SUCCESS;
4408 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4409 &hukey, TRUE);
4410 if (rc != ERROR_SUCCESS)
4411 goto end;
4413 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4414 NULL, &hudkey, TRUE);
4415 if (rc != ERROR_SUCCESS)
4416 goto end;
4418 rc = msi_publish_upgrade_code(package);
4419 if (rc != ERROR_SUCCESS)
4420 goto end;
4422 rc = msi_publish_product_properties(package, hukey);
4423 if (rc != ERROR_SUCCESS)
4424 goto end;
4426 rc = msi_publish_sourcelist(package, hukey);
4427 if (rc != ERROR_SUCCESS)
4428 goto end;
4430 rc = msi_publish_icons(package);
4432 end:
4433 uirow = MSI_CreateRecord( 1 );
4434 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4435 msi_ui_actiondata( package, szPublishProduct, uirow );
4436 msiobj_release( &uirow->hdr );
4438 RegCloseKey(hukey);
4439 RegCloseKey(hudkey);
4440 return rc;
4443 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4445 WCHAR *filename, *ptr, *folder, *ret;
4446 const WCHAR *dirprop;
4448 filename = msi_dup_record_field( row, 2 );
4449 if (filename && (ptr = strchrW( filename, '|' )))
4450 ptr++;
4451 else
4452 ptr = filename;
4454 dirprop = MSI_RecordGetString( row, 3 );
4455 if (dirprop)
4457 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4458 if (!folder) folder = msi_dup_property( package->db, dirprop );
4460 else
4461 folder = msi_dup_property( package->db, szWindowsFolder );
4463 if (!folder)
4465 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4466 msi_free( filename );
4467 return NULL;
4470 ret = msi_build_directory_name( 2, folder, ptr );
4472 msi_free( filename );
4473 msi_free( folder );
4474 return ret;
4477 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4479 MSIPACKAGE *package = param;
4480 LPCWSTR component, section, key, value, identifier;
4481 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4482 MSIRECORD * uirow;
4483 INT action;
4484 MSICOMPONENT *comp;
4486 component = MSI_RecordGetString(row, 8);
4487 comp = msi_get_loaded_component(package,component);
4488 if (!comp)
4489 return ERROR_SUCCESS;
4491 comp->Action = msi_get_component_action( package, comp );
4492 if (comp->Action != INSTALLSTATE_LOCAL)
4494 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4495 return ERROR_SUCCESS;
4498 identifier = MSI_RecordGetString(row,1);
4499 section = MSI_RecordGetString(row,4);
4500 key = MSI_RecordGetString(row,5);
4501 value = MSI_RecordGetString(row,6);
4502 action = MSI_RecordGetInteger(row,7);
4504 deformat_string(package,section,&deformated_section);
4505 deformat_string(package,key,&deformated_key);
4506 deformat_string(package,value,&deformated_value);
4508 fullname = get_ini_file_name(package, row);
4510 if (action == 0)
4512 TRACE("Adding value %s to section %s in %s\n",
4513 debugstr_w(deformated_key), debugstr_w(deformated_section),
4514 debugstr_w(fullname));
4515 WritePrivateProfileStringW(deformated_section, deformated_key,
4516 deformated_value, fullname);
4518 else if (action == 1)
4520 WCHAR returned[10];
4521 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4522 returned, 10, fullname);
4523 if (returned[0] == 0)
4525 TRACE("Adding value %s to section %s in %s\n",
4526 debugstr_w(deformated_key), debugstr_w(deformated_section),
4527 debugstr_w(fullname));
4529 WritePrivateProfileStringW(deformated_section, deformated_key,
4530 deformated_value, fullname);
4533 else if (action == 3)
4534 FIXME("Append to existing section not yet implemented\n");
4536 uirow = MSI_CreateRecord(4);
4537 MSI_RecordSetStringW(uirow,1,identifier);
4538 MSI_RecordSetStringW(uirow,2,deformated_section);
4539 MSI_RecordSetStringW(uirow,3,deformated_key);
4540 MSI_RecordSetStringW(uirow,4,deformated_value);
4541 msi_ui_actiondata( package, szWriteIniValues, uirow );
4542 msiobj_release( &uirow->hdr );
4544 msi_free(fullname);
4545 msi_free(deformated_key);
4546 msi_free(deformated_value);
4547 msi_free(deformated_section);
4548 return ERROR_SUCCESS;
4551 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4553 static const WCHAR query[] = {
4554 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4555 '`','I','n','i','F','i','l','e','`',0};
4556 MSIQUERY *view;
4557 UINT rc;
4559 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4560 if (rc != ERROR_SUCCESS)
4561 return ERROR_SUCCESS;
4563 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4564 msiobj_release(&view->hdr);
4565 return rc;
4568 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4570 MSIPACKAGE *package = param;
4571 LPCWSTR component, section, key, value, identifier;
4572 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4573 MSICOMPONENT *comp;
4574 MSIRECORD *uirow;
4575 INT action;
4577 component = MSI_RecordGetString( row, 8 );
4578 comp = msi_get_loaded_component( package, component );
4579 if (!comp)
4580 return ERROR_SUCCESS;
4582 comp->Action = msi_get_component_action( package, comp );
4583 if (comp->Action != INSTALLSTATE_ABSENT)
4585 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4586 return ERROR_SUCCESS;
4589 identifier = MSI_RecordGetString( row, 1 );
4590 section = MSI_RecordGetString( row, 4 );
4591 key = MSI_RecordGetString( row, 5 );
4592 value = MSI_RecordGetString( row, 6 );
4593 action = MSI_RecordGetInteger( row, 7 );
4595 deformat_string( package, section, &deformated_section );
4596 deformat_string( package, key, &deformated_key );
4597 deformat_string( package, value, &deformated_value );
4599 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4601 filename = get_ini_file_name( package, row );
4603 TRACE("Removing key %s from section %s in %s\n",
4604 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4606 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4608 WARN("Unable to remove key %u\n", GetLastError());
4610 msi_free( filename );
4612 else
4613 FIXME("Unsupported action %d\n", action);
4616 uirow = MSI_CreateRecord( 4 );
4617 MSI_RecordSetStringW( uirow, 1, identifier );
4618 MSI_RecordSetStringW( uirow, 2, deformated_section );
4619 MSI_RecordSetStringW( uirow, 3, deformated_key );
4620 MSI_RecordSetStringW( uirow, 4, deformated_value );
4621 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4622 msiobj_release( &uirow->hdr );
4624 msi_free( deformated_key );
4625 msi_free( deformated_value );
4626 msi_free( deformated_section );
4627 return ERROR_SUCCESS;
4630 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4632 MSIPACKAGE *package = param;
4633 LPCWSTR component, section, key, value, identifier;
4634 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4635 MSICOMPONENT *comp;
4636 MSIRECORD *uirow;
4637 INT action;
4639 component = MSI_RecordGetString( row, 8 );
4640 comp = msi_get_loaded_component( package, component );
4641 if (!comp)
4642 return ERROR_SUCCESS;
4644 comp->Action = msi_get_component_action( package, comp );
4645 if (comp->Action != INSTALLSTATE_LOCAL)
4647 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4648 return ERROR_SUCCESS;
4651 identifier = MSI_RecordGetString( row, 1 );
4652 section = MSI_RecordGetString( row, 4 );
4653 key = MSI_RecordGetString( row, 5 );
4654 value = MSI_RecordGetString( row, 6 );
4655 action = MSI_RecordGetInteger( row, 7 );
4657 deformat_string( package, section, &deformated_section );
4658 deformat_string( package, key, &deformated_key );
4659 deformat_string( package, value, &deformated_value );
4661 if (action == msidbIniFileActionRemoveLine)
4663 filename = get_ini_file_name( package, row );
4665 TRACE("Removing key %s from section %s in %s\n",
4666 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4668 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4670 WARN("Unable to remove key %u\n", GetLastError());
4672 msi_free( filename );
4674 else
4675 FIXME("Unsupported action %d\n", action);
4677 uirow = MSI_CreateRecord( 4 );
4678 MSI_RecordSetStringW( uirow, 1, identifier );
4679 MSI_RecordSetStringW( uirow, 2, deformated_section );
4680 MSI_RecordSetStringW( uirow, 3, deformated_key );
4681 MSI_RecordSetStringW( uirow, 4, deformated_value );
4682 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4683 msiobj_release( &uirow->hdr );
4685 msi_free( deformated_key );
4686 msi_free( deformated_value );
4687 msi_free( deformated_section );
4688 return ERROR_SUCCESS;
4691 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4693 static const WCHAR query[] = {
4694 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4695 '`','I','n','i','F','i','l','e','`',0};
4696 static const WCHAR remove_query[] = {
4697 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4698 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4699 MSIQUERY *view;
4700 UINT rc;
4702 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4703 if (rc == ERROR_SUCCESS)
4705 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4706 msiobj_release( &view->hdr );
4707 if (rc != ERROR_SUCCESS)
4708 return rc;
4710 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4711 if (rc == ERROR_SUCCESS)
4713 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4714 msiobj_release( &view->hdr );
4715 if (rc != ERROR_SUCCESS)
4716 return rc;
4718 return ERROR_SUCCESS;
4721 static void register_dll( const WCHAR *dll, BOOL unregister )
4723 static const WCHAR regW[] =
4724 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4725 static const WCHAR unregW[] =
4726 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4727 PROCESS_INFORMATION pi;
4728 STARTUPINFOW si;
4729 WCHAR *cmd;
4731 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4733 if (unregister) sprintfW( cmd, unregW, dll );
4734 else sprintfW( cmd, regW, dll );
4736 memset( &si, 0, sizeof(STARTUPINFOW) );
4737 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4739 CloseHandle( pi.hThread );
4740 msi_dialog_check_messages( pi.hProcess );
4741 CloseHandle( pi.hProcess );
4743 msi_free( cmd );
4746 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4748 MSIPACKAGE *package = param;
4749 LPCWSTR filename;
4750 MSIFILE *file;
4751 MSIRECORD *uirow;
4753 filename = MSI_RecordGetString( row, 1 );
4754 file = msi_get_loaded_file( package, filename );
4755 if (!file)
4757 WARN("unable to find file %s\n", debugstr_w(filename));
4758 return ERROR_SUCCESS;
4760 file->Component->Action = msi_get_component_action( package, file->Component );
4761 if (file->Component->Action != INSTALLSTATE_LOCAL)
4763 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4764 return ERROR_SUCCESS;
4767 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4768 register_dll( file->TargetPath, FALSE );
4770 uirow = MSI_CreateRecord( 2 );
4771 MSI_RecordSetStringW( uirow, 1, file->File );
4772 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4773 msi_ui_actiondata( package, szSelfRegModules, uirow );
4774 msiobj_release( &uirow->hdr );
4776 return ERROR_SUCCESS;
4779 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4781 static const WCHAR query[] = {
4782 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4783 '`','S','e','l','f','R','e','g','`',0};
4784 MSIQUERY *view;
4785 UINT rc;
4787 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4788 if (rc != ERROR_SUCCESS)
4789 return ERROR_SUCCESS;
4791 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4792 msiobj_release(&view->hdr);
4793 return rc;
4796 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4798 MSIPACKAGE *package = param;
4799 LPCWSTR filename;
4800 MSIFILE *file;
4801 MSIRECORD *uirow;
4803 filename = MSI_RecordGetString( row, 1 );
4804 file = msi_get_loaded_file( package, filename );
4805 if (!file)
4807 WARN("unable to find file %s\n", debugstr_w(filename));
4808 return ERROR_SUCCESS;
4810 file->Component->Action = msi_get_component_action( package, file->Component );
4811 if (file->Component->Action != INSTALLSTATE_ABSENT)
4813 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4814 return ERROR_SUCCESS;
4817 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4818 register_dll( file->TargetPath, TRUE );
4820 uirow = MSI_CreateRecord( 2 );
4821 MSI_RecordSetStringW( uirow, 1, file->File );
4822 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4823 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4824 msiobj_release( &uirow->hdr );
4826 return ERROR_SUCCESS;
4829 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4831 static const WCHAR query[] = {
4832 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4833 '`','S','e','l','f','R','e','g','`',0};
4834 MSIQUERY *view;
4835 UINT rc;
4837 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4838 if (rc != ERROR_SUCCESS)
4839 return ERROR_SUCCESS;
4841 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4842 msiobj_release( &view->hdr );
4843 return rc;
4846 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4848 MSIFEATURE *feature;
4849 UINT rc;
4850 HKEY hkey = NULL, userdata = NULL;
4852 if (!msi_check_publish(package))
4853 return ERROR_SUCCESS;
4855 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4856 &hkey, TRUE);
4857 if (rc != ERROR_SUCCESS)
4858 goto end;
4860 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4861 &userdata, TRUE);
4862 if (rc != ERROR_SUCCESS)
4863 goto end;
4865 /* here the guids are base 85 encoded */
4866 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4868 ComponentList *cl;
4869 LPWSTR data = NULL;
4870 GUID clsid;
4871 INT size;
4872 BOOL absent = FALSE;
4873 MSIRECORD *uirow;
4875 if (feature->Action != INSTALLSTATE_LOCAL &&
4876 feature->Action != INSTALLSTATE_SOURCE &&
4877 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4879 size = 1;
4880 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4882 size += 21;
4884 if (feature->Feature_Parent)
4885 size += strlenW( feature->Feature_Parent )+2;
4887 data = msi_alloc(size * sizeof(WCHAR));
4889 data[0] = 0;
4890 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4892 MSICOMPONENT* component = cl->component;
4893 WCHAR buf[21];
4895 buf[0] = 0;
4896 if (component->ComponentId)
4898 TRACE("From %s\n",debugstr_w(component->ComponentId));
4899 CLSIDFromString(component->ComponentId, &clsid);
4900 encode_base85_guid(&clsid,buf);
4901 TRACE("to %s\n",debugstr_w(buf));
4902 strcatW(data,buf);
4906 if (feature->Feature_Parent)
4908 static const WCHAR sep[] = {'\2',0};
4909 strcatW(data,sep);
4910 strcatW(data,feature->Feature_Parent);
4913 msi_reg_set_val_str( userdata, feature->Feature, data );
4914 msi_free(data);
4916 size = 0;
4917 if (feature->Feature_Parent)
4918 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4919 if (!absent)
4921 size += sizeof(WCHAR);
4922 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4923 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4925 else
4927 size += 2*sizeof(WCHAR);
4928 data = msi_alloc(size);
4929 data[0] = 0x6;
4930 data[1] = 0;
4931 if (feature->Feature_Parent)
4932 strcpyW( &data[1], feature->Feature_Parent );
4933 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4934 (LPBYTE)data,size);
4935 msi_free(data);
4938 /* the UI chunk */
4939 uirow = MSI_CreateRecord( 1 );
4940 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4941 msi_ui_actiondata( package, szPublishFeatures, uirow );
4942 msiobj_release( &uirow->hdr );
4943 /* FIXME: call msi_ui_progress? */
4946 end:
4947 RegCloseKey(hkey);
4948 RegCloseKey(userdata);
4949 return rc;
4952 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4954 UINT r;
4955 HKEY hkey;
4956 MSIRECORD *uirow;
4958 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4960 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4961 &hkey, FALSE);
4962 if (r == ERROR_SUCCESS)
4964 RegDeleteValueW(hkey, feature->Feature);
4965 RegCloseKey(hkey);
4968 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4969 &hkey, FALSE);
4970 if (r == ERROR_SUCCESS)
4972 RegDeleteValueW(hkey, feature->Feature);
4973 RegCloseKey(hkey);
4976 uirow = MSI_CreateRecord( 1 );
4977 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4978 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4979 msiobj_release( &uirow->hdr );
4981 return ERROR_SUCCESS;
4984 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4986 MSIFEATURE *feature;
4988 if (!msi_check_unpublish(package))
4989 return ERROR_SUCCESS;
4991 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4993 msi_unpublish_feature(package, feature);
4996 return ERROR_SUCCESS;
4999 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5001 SYSTEMTIME systime;
5002 DWORD size, langid;
5003 WCHAR date[9], *val, *buffer;
5004 const WCHAR *prop, *key;
5006 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5007 static const WCHAR modpath_fmt[] =
5008 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5009 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5010 static const WCHAR szModifyPath[] =
5011 {'M','o','d','i','f','y','P','a','t','h',0};
5012 static const WCHAR szUninstallString[] =
5013 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5014 static const WCHAR szEstimatedSize[] =
5015 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5016 static const WCHAR szDisplayVersion[] =
5017 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5018 static const WCHAR szInstallSource[] =
5019 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5020 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5021 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5022 static const WCHAR szAuthorizedCDFPrefix[] =
5023 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5024 static const WCHAR szARPCONTACT[] =
5025 {'A','R','P','C','O','N','T','A','C','T',0};
5026 static const WCHAR szContact[] =
5027 {'C','o','n','t','a','c','t',0};
5028 static const WCHAR szARPCOMMENTS[] =
5029 {'A','R','P','C','O','M','M','E','N','T','S',0};
5030 static const WCHAR szComments[] =
5031 {'C','o','m','m','e','n','t','s',0};
5032 static const WCHAR szProductName[] =
5033 {'P','r','o','d','u','c','t','N','a','m','e',0};
5034 static const WCHAR szDisplayName[] =
5035 {'D','i','s','p','l','a','y','N','a','m','e',0};
5036 static const WCHAR szARPHELPLINK[] =
5037 {'A','R','P','H','E','L','P','L','I','N','K',0};
5038 static const WCHAR szHelpLink[] =
5039 {'H','e','l','p','L','i','n','k',0};
5040 static const WCHAR szARPHELPTELEPHONE[] =
5041 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5042 static const WCHAR szHelpTelephone[] =
5043 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5044 static const WCHAR szARPINSTALLLOCATION[] =
5045 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5046 static const WCHAR szManufacturer[] =
5047 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5048 static const WCHAR szPublisher[] =
5049 {'P','u','b','l','i','s','h','e','r',0};
5050 static const WCHAR szARPREADME[] =
5051 {'A','R','P','R','E','A','D','M','E',0};
5052 static const WCHAR szReadme[] =
5053 {'R','e','a','d','M','e',0};
5054 static const WCHAR szARPSIZE[] =
5055 {'A','R','P','S','I','Z','E',0};
5056 static const WCHAR szSize[] =
5057 {'S','i','z','e',0};
5058 static const WCHAR szARPURLINFOABOUT[] =
5059 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5060 static const WCHAR szURLInfoAbout[] =
5061 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5062 static const WCHAR szARPURLUPDATEINFO[] =
5063 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5064 static const WCHAR szURLUpdateInfo[] =
5065 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5066 static const WCHAR szARPSYSTEMCOMPONENT[] =
5067 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5068 static const WCHAR szSystemComponent[] =
5069 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5071 static const WCHAR *propval[] = {
5072 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5073 szARPCONTACT, szContact,
5074 szARPCOMMENTS, szComments,
5075 szProductName, szDisplayName,
5076 szARPHELPLINK, szHelpLink,
5077 szARPHELPTELEPHONE, szHelpTelephone,
5078 szARPINSTALLLOCATION, szInstallLocation,
5079 szSourceDir, szInstallSource,
5080 szManufacturer, szPublisher,
5081 szARPREADME, szReadme,
5082 szARPSIZE, szSize,
5083 szARPURLINFOABOUT, szURLInfoAbout,
5084 szARPURLUPDATEINFO, szURLUpdateInfo,
5085 NULL
5087 const WCHAR **p = propval;
5089 while (*p)
5091 prop = *p++;
5092 key = *p++;
5093 val = msi_dup_property(package->db, prop);
5094 msi_reg_set_val_str(hkey, key, val);
5095 msi_free(val);
5098 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5099 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5101 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5103 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5104 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5105 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5106 msi_free(buffer);
5108 /* FIXME: Write real Estimated Size when we have it */
5109 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5111 GetLocalTime(&systime);
5112 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5113 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5115 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5116 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5118 buffer = msi_dup_property(package->db, szProductVersion);
5119 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5120 if (buffer)
5122 DWORD verdword = msi_version_str_to_dword(buffer);
5124 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5125 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5126 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5127 msi_free(buffer);
5130 return ERROR_SUCCESS;
5133 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5135 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5136 MSIRECORD *uirow;
5137 LPWSTR upgrade_code;
5138 HKEY hkey, props, upgrade_key;
5139 UINT rc;
5141 /* FIXME: also need to publish if the product is in advertise mode */
5142 if (!msi_check_publish(package))
5143 return ERROR_SUCCESS;
5145 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5146 if (rc != ERROR_SUCCESS)
5147 return rc;
5149 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5150 if (rc != ERROR_SUCCESS)
5151 goto done;
5153 rc = msi_publish_install_properties(package, hkey);
5154 if (rc != ERROR_SUCCESS)
5155 goto done;
5157 rc = msi_publish_install_properties(package, props);
5158 if (rc != ERROR_SUCCESS)
5159 goto done;
5161 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5162 if (upgrade_code)
5164 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5165 if (rc == ERROR_SUCCESS)
5167 squash_guid( package->ProductCode, squashed_pc );
5168 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5169 RegCloseKey( upgrade_key );
5171 msi_free( upgrade_code );
5173 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5174 package->delete_on_close = FALSE;
5176 done:
5177 uirow = MSI_CreateRecord( 1 );
5178 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5179 msi_ui_actiondata( package, szRegisterProduct, uirow );
5180 msiobj_release( &uirow->hdr );
5182 RegCloseKey(hkey);
5183 return ERROR_SUCCESS;
5186 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5188 return execute_script(package, SCRIPT_INSTALL);
5191 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5193 MSIPACKAGE *package = param;
5194 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5195 WCHAR *p, *icon_path;
5197 if (!icon) return ERROR_SUCCESS;
5198 if ((icon_path = msi_build_icon_path( package, icon )))
5200 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5201 DeleteFileW( icon_path );
5202 if ((p = strrchrW( icon_path, '\\' )))
5204 *p = 0;
5205 RemoveDirectoryW( icon_path );
5207 msi_free( icon_path );
5209 return ERROR_SUCCESS;
5212 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5214 static const WCHAR query[]= {
5215 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5216 MSIQUERY *view;
5217 UINT r;
5219 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5220 if (r == ERROR_SUCCESS)
5222 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5223 msiobj_release( &view->hdr );
5224 if (r != ERROR_SUCCESS)
5225 return r;
5227 return ERROR_SUCCESS;
5230 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5232 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5233 WCHAR *upgrade, **features;
5234 BOOL full_uninstall = TRUE;
5235 MSIFEATURE *feature;
5236 MSIPATCHINFO *patch;
5237 UINT i;
5239 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5241 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5243 features = msi_split_string( remove, ',' );
5244 for (i = 0; features && features[i]; i++)
5246 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5248 msi_free(features);
5250 if (!full_uninstall)
5251 return ERROR_SUCCESS;
5253 MSIREG_DeleteProductKey(package->ProductCode);
5254 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5255 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5257 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5258 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5259 MSIREG_DeleteUserProductKey(package->ProductCode);
5260 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5262 upgrade = msi_dup_property(package->db, szUpgradeCode);
5263 if (upgrade)
5265 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5266 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5267 msi_free(upgrade);
5270 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5272 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5273 if (!strcmpW( package->ProductCode, patch->products ))
5275 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5276 patch->delete_on_close = TRUE;
5278 /* FIXME: remove local patch package if this is the last product */
5280 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5281 package->delete_on_close = TRUE;
5283 msi_unpublish_icons( package );
5284 return ERROR_SUCCESS;
5287 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5289 UINT rc;
5290 WCHAR *remove;
5292 /* first do the same as an InstallExecute */
5293 rc = ACTION_InstallExecute(package);
5294 if (rc != ERROR_SUCCESS)
5295 return rc;
5297 /* then handle commit actions */
5298 rc = execute_script(package, SCRIPT_COMMIT);
5299 if (rc != ERROR_SUCCESS)
5300 return rc;
5302 remove = msi_dup_property(package->db, szRemove);
5303 rc = msi_unpublish_product(package, remove);
5304 msi_free(remove);
5305 return rc;
5308 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5310 static const WCHAR RunOnce[] = {
5311 'S','o','f','t','w','a','r','e','\\',
5312 'M','i','c','r','o','s','o','f','t','\\',
5313 'W','i','n','d','o','w','s','\\',
5314 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5315 'R','u','n','O','n','c','e',0};
5316 static const WCHAR InstallRunOnce[] = {
5317 'S','o','f','t','w','a','r','e','\\',
5318 'M','i','c','r','o','s','o','f','t','\\',
5319 'W','i','n','d','o','w','s','\\',
5320 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5321 'I','n','s','t','a','l','l','e','r','\\',
5322 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5324 static const WCHAR msiexec_fmt[] = {
5325 '%','s',
5326 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5327 '\"','%','s','\"',0};
5328 static const WCHAR install_fmt[] = {
5329 '/','I',' ','\"','%','s','\"',' ',
5330 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5331 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5332 WCHAR buffer[256], sysdir[MAX_PATH];
5333 HKEY hkey;
5334 WCHAR squished_pc[100];
5336 squash_guid(package->ProductCode,squished_pc);
5338 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5339 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5340 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5341 squished_pc);
5343 msi_reg_set_val_str( hkey, squished_pc, buffer );
5344 RegCloseKey(hkey);
5346 TRACE("Reboot command %s\n",debugstr_w(buffer));
5348 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5349 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5351 msi_reg_set_val_str( hkey, squished_pc, buffer );
5352 RegCloseKey(hkey);
5354 return ERROR_INSTALL_SUSPEND;
5357 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5359 static const WCHAR query[] =
5360 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5361 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5362 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5363 MSIRECORD *rec, *row;
5364 DWORD i, size = 0;
5365 va_list va;
5366 const WCHAR *str;
5367 WCHAR *data;
5369 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5371 rec = MSI_CreateRecord( count + 2 );
5372 str = MSI_RecordGetString( row, 1 );
5373 MSI_RecordSetStringW( rec, 0, str );
5374 msiobj_release( &row->hdr );
5375 MSI_RecordSetInteger( rec, 1, error );
5377 va_start( va, count );
5378 for (i = 0; i < count; i++)
5380 str = va_arg( va, const WCHAR *);
5381 MSI_RecordSetStringW( rec, i + 2, str );
5383 va_end( va );
5385 MSI_FormatRecordW( package, rec, NULL, &size );
5386 size++;
5387 data = msi_alloc( size * sizeof(WCHAR) );
5388 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5389 else data[0] = 0;
5390 msiobj_release( &rec->hdr );
5391 return data;
5394 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5396 DWORD attrib;
5397 UINT rc;
5400 * We are currently doing what should be done here in the top level Install
5401 * however for Administrative and uninstalls this step will be needed
5403 if (!package->PackagePath)
5404 return ERROR_SUCCESS;
5406 msi_set_sourcedir_props(package, TRUE);
5408 attrib = GetFileAttributesW(package->db->path);
5409 if (attrib == INVALID_FILE_ATTRIBUTES)
5411 LPWSTR prompt, msg;
5412 DWORD size = 0;
5414 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5415 package->Context, MSICODE_PRODUCT,
5416 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5417 if (rc == ERROR_MORE_DATA)
5419 prompt = msi_alloc(size * sizeof(WCHAR));
5420 MsiSourceListGetInfoW(package->ProductCode, NULL,
5421 package->Context, MSICODE_PRODUCT,
5422 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5424 else
5425 prompt = strdupW(package->db->path);
5427 msg = msi_build_error_string(package, 1302, 1, prompt);
5428 msi_free(prompt);
5429 while(attrib == INVALID_FILE_ATTRIBUTES)
5431 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5432 if (rc == IDCANCEL)
5434 msi_free(msg);
5435 return ERROR_INSTALL_USEREXIT;
5437 attrib = GetFileAttributesW(package->db->path);
5439 msi_free(msg);
5440 rc = ERROR_SUCCESS;
5442 else
5443 return ERROR_SUCCESS;
5445 return rc;
5448 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5450 HKEY hkey = 0;
5451 LPWSTR buffer, productid = NULL;
5452 UINT i, rc = ERROR_SUCCESS;
5453 MSIRECORD *uirow;
5455 static const WCHAR szPropKeys[][80] =
5457 {'P','r','o','d','u','c','t','I','D',0},
5458 {'U','S','E','R','N','A','M','E',0},
5459 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5460 {0},
5463 static const WCHAR szRegKeys[][80] =
5465 {'P','r','o','d','u','c','t','I','D',0},
5466 {'R','e','g','O','w','n','e','r',0},
5467 {'R','e','g','C','o','m','p','a','n','y',0},
5468 {0},
5471 if (msi_check_unpublish(package))
5473 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5474 goto end;
5477 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5478 if (!productid)
5479 goto end;
5481 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5482 NULL, &hkey, TRUE);
5483 if (rc != ERROR_SUCCESS)
5484 goto end;
5486 for( i = 0; szPropKeys[i][0]; i++ )
5488 buffer = msi_dup_property( package->db, szPropKeys[i] );
5489 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5490 msi_free( buffer );
5493 end:
5494 uirow = MSI_CreateRecord( 1 );
5495 MSI_RecordSetStringW( uirow, 1, productid );
5496 msi_ui_actiondata( package, szRegisterUser, uirow );
5497 msiobj_release( &uirow->hdr );
5499 msi_free(productid);
5500 RegCloseKey(hkey);
5501 return rc;
5505 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5507 UINT rc;
5509 package->script->InWhatSequence |= SEQUENCE_EXEC;
5510 rc = ACTION_ProcessExecSequence(package,FALSE);
5511 return rc;
5514 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5516 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5517 WCHAR productid_85[21], component_85[21], *ret;
5518 GUID clsid;
5519 DWORD sz;
5521 /* > is used if there is a component GUID and < if not. */
5523 productid_85[0] = 0;
5524 component_85[0] = 0;
5525 CLSIDFromString( package->ProductCode, &clsid );
5527 encode_base85_guid( &clsid, productid_85 );
5528 if (component)
5530 CLSIDFromString( component->ComponentId, &clsid );
5531 encode_base85_guid( &clsid, component_85 );
5534 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5535 debugstr_w(component_85));
5537 sz = 20 + strlenW( feature ) + 20 + 3;
5538 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5539 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5540 return ret;
5543 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5545 MSIPACKAGE *package = param;
5546 LPCWSTR compgroupid, component, feature, qualifier, text;
5547 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5548 HKEY hkey = NULL;
5549 UINT rc;
5550 MSICOMPONENT *comp;
5551 MSIFEATURE *feat;
5552 DWORD sz;
5553 MSIRECORD *uirow;
5554 int len;
5556 feature = MSI_RecordGetString(rec, 5);
5557 feat = msi_get_loaded_feature(package, feature);
5558 if (!feat)
5559 return ERROR_SUCCESS;
5561 feat->Action = msi_get_feature_action( package, feat );
5562 if (feat->Action != INSTALLSTATE_LOCAL &&
5563 feat->Action != INSTALLSTATE_SOURCE &&
5564 feat->Action != INSTALLSTATE_ADVERTISED)
5566 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5567 return ERROR_SUCCESS;
5570 component = MSI_RecordGetString(rec, 3);
5571 comp = msi_get_loaded_component(package, component);
5572 if (!comp)
5573 return ERROR_SUCCESS;
5575 compgroupid = MSI_RecordGetString(rec,1);
5576 qualifier = MSI_RecordGetString(rec,2);
5578 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5579 if (rc != ERROR_SUCCESS)
5580 goto end;
5582 advertise = msi_create_component_advertise_string( package, comp, feature );
5583 text = MSI_RecordGetString( rec, 4 );
5584 if (text)
5586 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5587 strcpyW( p, advertise );
5588 strcatW( p, text );
5589 msi_free( advertise );
5590 advertise = p;
5592 existing = msi_reg_get_val_str( hkey, qualifier );
5594 sz = strlenW( advertise ) + 1;
5595 if (existing)
5597 for (p = existing; *p; p += len)
5599 len = strlenW( p ) + 1;
5600 if (strcmpW( advertise, p )) sz += len;
5603 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5605 rc = ERROR_OUTOFMEMORY;
5606 goto end;
5608 q = output;
5609 if (existing)
5611 for (p = existing; *p; p += len)
5613 len = strlenW( p ) + 1;
5614 if (strcmpW( advertise, p ))
5616 memcpy( q, p, len * sizeof(WCHAR) );
5617 q += len;
5621 strcpyW( q, advertise );
5622 q[strlenW( q ) + 1] = 0;
5624 msi_reg_set_val_multi_str( hkey, qualifier, output );
5626 end:
5627 RegCloseKey(hkey);
5628 msi_free( output );
5629 msi_free( advertise );
5630 msi_free( existing );
5632 /* the UI chunk */
5633 uirow = MSI_CreateRecord( 2 );
5634 MSI_RecordSetStringW( uirow, 1, compgroupid );
5635 MSI_RecordSetStringW( uirow, 2, qualifier);
5636 msi_ui_actiondata( package, szPublishComponents, uirow );
5637 msiobj_release( &uirow->hdr );
5638 /* FIXME: call ui_progress? */
5640 return rc;
5644 * At present I am ignorning the advertised components part of this and only
5645 * focusing on the qualified component sets
5647 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5649 static const WCHAR query[] = {
5650 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5651 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5652 MSIQUERY *view;
5653 UINT rc;
5655 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5656 if (rc != ERROR_SUCCESS)
5657 return ERROR_SUCCESS;
5659 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5660 msiobj_release(&view->hdr);
5661 return rc;
5664 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5666 static const WCHAR szInstallerComponents[] = {
5667 'S','o','f','t','w','a','r','e','\\',
5668 'M','i','c','r','o','s','o','f','t','\\',
5669 'I','n','s','t','a','l','l','e','r','\\',
5670 'C','o','m','p','o','n','e','n','t','s','\\',0};
5672 MSIPACKAGE *package = param;
5673 LPCWSTR compgroupid, component, feature, qualifier;
5674 MSICOMPONENT *comp;
5675 MSIFEATURE *feat;
5676 MSIRECORD *uirow;
5677 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5678 LONG res;
5680 feature = MSI_RecordGetString( rec, 5 );
5681 feat = msi_get_loaded_feature( package, feature );
5682 if (!feat)
5683 return ERROR_SUCCESS;
5685 feat->Action = msi_get_feature_action( package, feat );
5686 if (feat->Action != INSTALLSTATE_ABSENT)
5688 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5689 return ERROR_SUCCESS;
5692 component = MSI_RecordGetString( rec, 3 );
5693 comp = msi_get_loaded_component( package, component );
5694 if (!comp)
5695 return ERROR_SUCCESS;
5697 compgroupid = MSI_RecordGetString( rec, 1 );
5698 qualifier = MSI_RecordGetString( rec, 2 );
5700 squash_guid( compgroupid, squashed );
5701 strcpyW( keypath, szInstallerComponents );
5702 strcatW( keypath, squashed );
5704 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5705 if (res != ERROR_SUCCESS)
5707 WARN("Unable to delete component key %d\n", res);
5710 uirow = MSI_CreateRecord( 2 );
5711 MSI_RecordSetStringW( uirow, 1, compgroupid );
5712 MSI_RecordSetStringW( uirow, 2, qualifier );
5713 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5714 msiobj_release( &uirow->hdr );
5716 return ERROR_SUCCESS;
5719 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5721 static const WCHAR query[] = {
5722 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5723 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5724 MSIQUERY *view;
5725 UINT rc;
5727 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5728 if (rc != ERROR_SUCCESS)
5729 return ERROR_SUCCESS;
5731 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5732 msiobj_release( &view->hdr );
5733 return rc;
5736 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5738 static const WCHAR query[] =
5739 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5740 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5741 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5742 MSIPACKAGE *package = param;
5743 MSICOMPONENT *component;
5744 MSIRECORD *row;
5745 MSIFILE *file;
5746 SC_HANDLE hscm = NULL, service = NULL;
5747 LPCWSTR comp, key;
5748 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5749 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5750 DWORD serv_type, start_type, err_control;
5751 SERVICE_DESCRIPTIONW sd = {NULL};
5752 UINT ret = ERROR_SUCCESS;
5754 comp = MSI_RecordGetString( rec, 12 );
5755 component = msi_get_loaded_component( package, comp );
5756 if (!component)
5758 WARN("service component not found\n");
5759 goto done;
5761 component->Action = msi_get_component_action( package, component );
5762 if (component->Action != INSTALLSTATE_LOCAL)
5764 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5765 goto done;
5767 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5768 if (!hscm)
5770 ERR("Failed to open the SC Manager!\n");
5771 goto done;
5774 start_type = MSI_RecordGetInteger(rec, 5);
5775 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5776 goto done;
5778 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5779 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5780 serv_type = MSI_RecordGetInteger(rec, 4);
5781 err_control = MSI_RecordGetInteger(rec, 6);
5782 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5783 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5784 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5785 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5786 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5787 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5789 /* fetch the service path */
5790 row = MSI_QueryGetRecord(package->db, query, comp);
5791 if (!row)
5793 ERR("Query failed\n");
5794 goto done;
5796 key = MSI_RecordGetString(row, 6);
5797 file = msi_get_loaded_file(package, key);
5798 msiobj_release(&row->hdr);
5799 if (!file)
5801 ERR("Failed to load the service file\n");
5802 goto done;
5805 if (!args || !args[0]) image_path = file->TargetPath;
5806 else
5808 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5809 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5811 ret = ERROR_OUTOFMEMORY;
5812 goto done;
5815 strcpyW(image_path, file->TargetPath);
5816 strcatW(image_path, szSpace);
5817 strcatW(image_path, args);
5819 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5820 start_type, err_control, image_path, load_order,
5821 NULL, depends, serv_name, pass);
5823 if (!service)
5825 if (GetLastError() != ERROR_SERVICE_EXISTS)
5826 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5828 else if (sd.lpDescription)
5830 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5831 WARN("failed to set service description %u\n", GetLastError());
5834 if (image_path != file->TargetPath) msi_free(image_path);
5835 done:
5836 CloseServiceHandle(service);
5837 CloseServiceHandle(hscm);
5838 msi_free(name);
5839 msi_free(disp);
5840 msi_free(sd.lpDescription);
5841 msi_free(load_order);
5842 msi_free(serv_name);
5843 msi_free(pass);
5844 msi_free(depends);
5845 msi_free(args);
5847 return ret;
5850 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5852 static const WCHAR query[] = {
5853 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5854 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5855 MSIQUERY *view;
5856 UINT rc;
5858 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5859 if (rc != ERROR_SUCCESS)
5860 return ERROR_SUCCESS;
5862 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5863 msiobj_release(&view->hdr);
5864 return rc;
5867 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5868 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5870 LPCWSTR *vector, *temp_vector;
5871 LPWSTR p, q;
5872 DWORD sep_len;
5874 static const WCHAR separator[] = {'[','~',']',0};
5876 *numargs = 0;
5877 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5879 if (!args)
5880 return NULL;
5882 vector = msi_alloc(sizeof(LPWSTR));
5883 if (!vector)
5884 return NULL;
5886 p = args;
5889 (*numargs)++;
5890 vector[*numargs - 1] = p;
5892 if ((q = strstrW(p, separator)))
5894 *q = '\0';
5896 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5897 if (!temp_vector)
5899 msi_free(vector);
5900 return NULL;
5902 vector = temp_vector;
5904 p = q + sep_len;
5906 } while (q);
5908 return vector;
5911 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5913 MSIPACKAGE *package = param;
5914 MSICOMPONENT *comp;
5915 MSIRECORD *uirow;
5916 SC_HANDLE scm = NULL, service = NULL;
5917 LPCWSTR component, *vector = NULL;
5918 LPWSTR name, args, display_name = NULL;
5919 DWORD event, numargs, len, wait, dummy;
5920 UINT r = ERROR_FUNCTION_FAILED;
5921 SERVICE_STATUS_PROCESS status;
5922 ULONGLONG start_time;
5924 component = MSI_RecordGetString(rec, 6);
5925 comp = msi_get_loaded_component(package, component);
5926 if (!comp)
5927 return ERROR_SUCCESS;
5929 comp->Action = msi_get_component_action( package, comp );
5930 if (comp->Action != INSTALLSTATE_LOCAL)
5932 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5933 return ERROR_SUCCESS;
5936 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5937 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5938 event = MSI_RecordGetInteger(rec, 3);
5939 wait = MSI_RecordGetInteger(rec, 5);
5941 if (!(event & msidbServiceControlEventStart))
5943 r = ERROR_SUCCESS;
5944 goto done;
5947 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5948 if (!scm)
5950 ERR("Failed to open the service control manager\n");
5951 goto done;
5954 len = 0;
5955 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5956 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5958 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5959 GetServiceDisplayNameW( scm, name, display_name, &len );
5962 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5963 if (!service)
5965 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5966 goto done;
5969 vector = msi_service_args_to_vector(args, &numargs);
5971 if (!StartServiceW(service, numargs, vector) &&
5972 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5974 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5975 goto done;
5978 r = ERROR_SUCCESS;
5979 if (wait)
5981 /* wait for at most 30 seconds for the service to be up and running */
5982 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5983 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5985 TRACE("failed to query service status (%u)\n", GetLastError());
5986 goto done;
5988 start_time = GetTickCount64();
5989 while (status.dwCurrentState == SERVICE_START_PENDING)
5991 if (GetTickCount64() - start_time > 30000) break;
5992 Sleep(1000);
5993 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5994 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5996 TRACE("failed to query service status (%u)\n", GetLastError());
5997 goto done;
6000 if (status.dwCurrentState != SERVICE_RUNNING)
6002 WARN("service failed to start %u\n", status.dwCurrentState);
6003 r = ERROR_FUNCTION_FAILED;
6007 done:
6008 uirow = MSI_CreateRecord( 2 );
6009 MSI_RecordSetStringW( uirow, 1, display_name );
6010 MSI_RecordSetStringW( uirow, 2, name );
6011 msi_ui_actiondata( package, szStartServices, uirow );
6012 msiobj_release( &uirow->hdr );
6014 CloseServiceHandle(service);
6015 CloseServiceHandle(scm);
6017 msi_free(name);
6018 msi_free(args);
6019 msi_free(vector);
6020 msi_free(display_name);
6021 return r;
6024 static UINT ACTION_StartServices( MSIPACKAGE *package )
6026 static const WCHAR query[] = {
6027 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6028 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6029 MSIQUERY *view;
6030 UINT rc;
6032 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6033 if (rc != ERROR_SUCCESS)
6034 return ERROR_SUCCESS;
6036 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6037 msiobj_release(&view->hdr);
6038 return rc;
6041 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6043 DWORD i, needed, count;
6044 ENUM_SERVICE_STATUSW *dependencies;
6045 SERVICE_STATUS ss;
6046 SC_HANDLE depserv;
6047 BOOL stopped, ret = FALSE;
6049 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6050 0, &needed, &count))
6051 return TRUE;
6053 if (GetLastError() != ERROR_MORE_DATA)
6054 return FALSE;
6056 dependencies = msi_alloc(needed);
6057 if (!dependencies)
6058 return FALSE;
6060 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6061 needed, &needed, &count))
6062 goto done;
6064 for (i = 0; i < count; i++)
6066 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6067 SERVICE_STOP | SERVICE_QUERY_STATUS);
6068 if (!depserv)
6069 goto done;
6071 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6072 CloseServiceHandle(depserv);
6073 if (!stopped)
6074 goto done;
6077 ret = TRUE;
6079 done:
6080 msi_free(dependencies);
6081 return ret;
6084 static UINT stop_service( LPCWSTR name )
6086 SC_HANDLE scm = NULL, service = NULL;
6087 SERVICE_STATUS status;
6088 SERVICE_STATUS_PROCESS ssp;
6089 DWORD needed;
6091 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6092 if (!scm)
6094 WARN("Failed to open the SCM: %d\n", GetLastError());
6095 goto done;
6098 service = OpenServiceW(scm, name,
6099 SERVICE_STOP |
6100 SERVICE_QUERY_STATUS |
6101 SERVICE_ENUMERATE_DEPENDENTS);
6102 if (!service)
6104 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6105 goto done;
6108 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6109 sizeof(SERVICE_STATUS_PROCESS), &needed))
6111 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6112 goto done;
6115 if (ssp.dwCurrentState == SERVICE_STOPPED)
6116 goto done;
6118 stop_service_dependents(scm, service);
6120 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6121 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6123 done:
6124 CloseServiceHandle(service);
6125 CloseServiceHandle(scm);
6127 return ERROR_SUCCESS;
6130 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6132 MSIPACKAGE *package = param;
6133 MSICOMPONENT *comp;
6134 MSIRECORD *uirow;
6135 LPCWSTR component;
6136 LPWSTR name = NULL, display_name = NULL;
6137 DWORD event, len;
6138 SC_HANDLE scm;
6140 event = MSI_RecordGetInteger( rec, 3 );
6141 if (!(event & msidbServiceControlEventStop))
6142 return ERROR_SUCCESS;
6144 component = MSI_RecordGetString( rec, 6 );
6145 comp = msi_get_loaded_component( package, component );
6146 if (!comp)
6147 return ERROR_SUCCESS;
6149 comp->Action = msi_get_component_action( package, comp );
6150 if (comp->Action != INSTALLSTATE_ABSENT)
6152 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6153 return ERROR_SUCCESS;
6156 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6157 if (!scm)
6159 ERR("Failed to open the service control manager\n");
6160 goto done;
6163 len = 0;
6164 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6165 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6167 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6168 GetServiceDisplayNameW( scm, name, display_name, &len );
6170 CloseServiceHandle( scm );
6172 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6173 stop_service( name );
6175 done:
6176 uirow = MSI_CreateRecord( 2 );
6177 MSI_RecordSetStringW( uirow, 1, display_name );
6178 MSI_RecordSetStringW( uirow, 2, name );
6179 msi_ui_actiondata( package, szStopServices, uirow );
6180 msiobj_release( &uirow->hdr );
6182 msi_free( name );
6183 msi_free( display_name );
6184 return ERROR_SUCCESS;
6187 static UINT ACTION_StopServices( MSIPACKAGE *package )
6189 static const WCHAR query[] = {
6190 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6191 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6192 MSIQUERY *view;
6193 UINT rc;
6195 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6196 if (rc != ERROR_SUCCESS)
6197 return ERROR_SUCCESS;
6199 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6200 msiobj_release(&view->hdr);
6201 return rc;
6204 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6206 MSIPACKAGE *package = param;
6207 MSICOMPONENT *comp;
6208 MSIRECORD *uirow;
6209 LPWSTR name = NULL, display_name = NULL;
6210 DWORD event, len;
6211 SC_HANDLE scm = NULL, service = NULL;
6213 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6214 if (!comp)
6215 return ERROR_SUCCESS;
6217 event = MSI_RecordGetInteger( rec, 3 );
6218 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6220 comp->Action = msi_get_component_action( package, comp );
6221 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6222 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6224 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6225 msi_free( name );
6226 return ERROR_SUCCESS;
6228 stop_service( name );
6230 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6231 if (!scm)
6233 WARN("Failed to open the SCM: %d\n", GetLastError());
6234 goto done;
6237 len = 0;
6238 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6239 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6241 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6242 GetServiceDisplayNameW( scm, name, display_name, &len );
6245 service = OpenServiceW( scm, name, DELETE );
6246 if (!service)
6248 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6249 goto done;
6252 if (!DeleteService( service ))
6253 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6255 done:
6256 uirow = MSI_CreateRecord( 2 );
6257 MSI_RecordSetStringW( uirow, 1, display_name );
6258 MSI_RecordSetStringW( uirow, 2, name );
6259 msi_ui_actiondata( package, szDeleteServices, uirow );
6260 msiobj_release( &uirow->hdr );
6262 CloseServiceHandle( service );
6263 CloseServiceHandle( scm );
6264 msi_free( name );
6265 msi_free( display_name );
6267 return ERROR_SUCCESS;
6270 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6272 static const WCHAR query[] = {
6273 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6274 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6275 MSIQUERY *view;
6276 UINT rc;
6278 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6279 if (rc != ERROR_SUCCESS)
6280 return ERROR_SUCCESS;
6282 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6283 msiobj_release( &view->hdr );
6284 return rc;
6287 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6289 MSIPACKAGE *package = param;
6290 LPWSTR driver, driver_path, ptr;
6291 WCHAR outpath[MAX_PATH];
6292 MSIFILE *driver_file = NULL, *setup_file = NULL;
6293 MSICOMPONENT *comp;
6294 MSIRECORD *uirow;
6295 LPCWSTR desc, file_key, component;
6296 DWORD len, usage;
6297 UINT r = ERROR_SUCCESS;
6299 static const WCHAR driver_fmt[] = {
6300 'D','r','i','v','e','r','=','%','s',0};
6301 static const WCHAR setup_fmt[] = {
6302 'S','e','t','u','p','=','%','s',0};
6303 static const WCHAR usage_fmt[] = {
6304 'F','i','l','e','U','s','a','g','e','=','1',0};
6306 component = MSI_RecordGetString( rec, 2 );
6307 comp = msi_get_loaded_component( package, component );
6308 if (!comp)
6309 return ERROR_SUCCESS;
6311 comp->Action = msi_get_component_action( package, comp );
6312 if (comp->Action != INSTALLSTATE_LOCAL)
6314 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6315 return ERROR_SUCCESS;
6317 desc = MSI_RecordGetString(rec, 3);
6319 file_key = MSI_RecordGetString( rec, 4 );
6320 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6322 file_key = MSI_RecordGetString( rec, 5 );
6323 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6325 if (!driver_file)
6327 ERR("ODBC Driver entry not found!\n");
6328 return ERROR_FUNCTION_FAILED;
6331 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6332 if (setup_file)
6333 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6334 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6336 driver = msi_alloc(len * sizeof(WCHAR));
6337 if (!driver)
6338 return ERROR_OUTOFMEMORY;
6340 ptr = driver;
6341 lstrcpyW(ptr, desc);
6342 ptr += lstrlenW(ptr) + 1;
6344 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6345 ptr += len + 1;
6347 if (setup_file)
6349 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6350 ptr += len + 1;
6353 lstrcpyW(ptr, usage_fmt);
6354 ptr += lstrlenW(ptr) + 1;
6355 *ptr = '\0';
6357 if (!driver_file->TargetPath)
6359 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6360 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6362 driver_path = strdupW(driver_file->TargetPath);
6363 ptr = strrchrW(driver_path, '\\');
6364 if (ptr) *ptr = '\0';
6366 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6367 NULL, ODBC_INSTALL_COMPLETE, &usage))
6369 ERR("Failed to install SQL driver!\n");
6370 r = ERROR_FUNCTION_FAILED;
6373 uirow = MSI_CreateRecord( 5 );
6374 MSI_RecordSetStringW( uirow, 1, desc );
6375 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6376 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6377 msi_ui_actiondata( package, szInstallODBC, uirow );
6378 msiobj_release( &uirow->hdr );
6380 msi_free(driver);
6381 msi_free(driver_path);
6383 return r;
6386 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6388 MSIPACKAGE *package = param;
6389 LPWSTR translator, translator_path, ptr;
6390 WCHAR outpath[MAX_PATH];
6391 MSIFILE *translator_file = NULL, *setup_file = NULL;
6392 MSICOMPONENT *comp;
6393 MSIRECORD *uirow;
6394 LPCWSTR desc, file_key, component;
6395 DWORD len, usage;
6396 UINT r = ERROR_SUCCESS;
6398 static const WCHAR translator_fmt[] = {
6399 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6400 static const WCHAR setup_fmt[] = {
6401 'S','e','t','u','p','=','%','s',0};
6403 component = MSI_RecordGetString( rec, 2 );
6404 comp = msi_get_loaded_component( package, component );
6405 if (!comp)
6406 return ERROR_SUCCESS;
6408 comp->Action = msi_get_component_action( package, comp );
6409 if (comp->Action != INSTALLSTATE_LOCAL)
6411 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6412 return ERROR_SUCCESS;
6414 desc = MSI_RecordGetString(rec, 3);
6416 file_key = MSI_RecordGetString( rec, 4 );
6417 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6419 file_key = MSI_RecordGetString( rec, 5 );
6420 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6422 if (!translator_file)
6424 ERR("ODBC Translator entry not found!\n");
6425 return ERROR_FUNCTION_FAILED;
6428 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6429 if (setup_file)
6430 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6432 translator = msi_alloc(len * sizeof(WCHAR));
6433 if (!translator)
6434 return ERROR_OUTOFMEMORY;
6436 ptr = translator;
6437 lstrcpyW(ptr, desc);
6438 ptr += lstrlenW(ptr) + 1;
6440 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6441 ptr += len + 1;
6443 if (setup_file)
6445 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6446 ptr += len + 1;
6448 *ptr = '\0';
6450 translator_path = strdupW(translator_file->TargetPath);
6451 ptr = strrchrW(translator_path, '\\');
6452 if (ptr) *ptr = '\0';
6454 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6455 NULL, ODBC_INSTALL_COMPLETE, &usage))
6457 ERR("Failed to install SQL translator!\n");
6458 r = ERROR_FUNCTION_FAILED;
6461 uirow = MSI_CreateRecord( 5 );
6462 MSI_RecordSetStringW( uirow, 1, desc );
6463 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6464 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6465 msi_ui_actiondata( package, szInstallODBC, uirow );
6466 msiobj_release( &uirow->hdr );
6468 msi_free(translator);
6469 msi_free(translator_path);
6471 return r;
6474 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6476 MSIPACKAGE *package = param;
6477 MSICOMPONENT *comp;
6478 LPWSTR attrs;
6479 LPCWSTR desc, driver, component;
6480 WORD request = ODBC_ADD_SYS_DSN;
6481 INT registration;
6482 DWORD len;
6483 UINT r = ERROR_SUCCESS;
6484 MSIRECORD *uirow;
6486 static const WCHAR attrs_fmt[] = {
6487 'D','S','N','=','%','s',0 };
6489 component = MSI_RecordGetString( rec, 2 );
6490 comp = msi_get_loaded_component( package, component );
6491 if (!comp)
6492 return ERROR_SUCCESS;
6494 comp->Action = msi_get_component_action( package, comp );
6495 if (comp->Action != INSTALLSTATE_LOCAL)
6497 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6498 return ERROR_SUCCESS;
6501 desc = MSI_RecordGetString(rec, 3);
6502 driver = MSI_RecordGetString(rec, 4);
6503 registration = MSI_RecordGetInteger(rec, 5);
6505 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6506 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6508 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6509 attrs = msi_alloc(len * sizeof(WCHAR));
6510 if (!attrs)
6511 return ERROR_OUTOFMEMORY;
6513 len = sprintfW(attrs, attrs_fmt, desc);
6514 attrs[len + 1] = 0;
6516 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6518 ERR("Failed to install SQL data source!\n");
6519 r = ERROR_FUNCTION_FAILED;
6522 uirow = MSI_CreateRecord( 5 );
6523 MSI_RecordSetStringW( uirow, 1, desc );
6524 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6525 MSI_RecordSetInteger( uirow, 3, request );
6526 msi_ui_actiondata( package, szInstallODBC, uirow );
6527 msiobj_release( &uirow->hdr );
6529 msi_free(attrs);
6531 return r;
6534 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6536 static const WCHAR driver_query[] = {
6537 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6538 'O','D','B','C','D','r','i','v','e','r',0};
6539 static const WCHAR translator_query[] = {
6540 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6541 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6542 static const WCHAR source_query[] = {
6543 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6544 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6545 MSIQUERY *view;
6546 UINT rc;
6548 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6549 if (rc == ERROR_SUCCESS)
6551 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6552 msiobj_release(&view->hdr);
6553 if (rc != ERROR_SUCCESS)
6554 return rc;
6556 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6557 if (rc == ERROR_SUCCESS)
6559 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6560 msiobj_release(&view->hdr);
6561 if (rc != ERROR_SUCCESS)
6562 return rc;
6564 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6565 if (rc == ERROR_SUCCESS)
6567 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6568 msiobj_release(&view->hdr);
6569 if (rc != ERROR_SUCCESS)
6570 return rc;
6572 return ERROR_SUCCESS;
6575 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6577 MSIPACKAGE *package = param;
6578 MSICOMPONENT *comp;
6579 MSIRECORD *uirow;
6580 DWORD usage;
6581 LPCWSTR desc, component;
6583 component = MSI_RecordGetString( rec, 2 );
6584 comp = msi_get_loaded_component( package, component );
6585 if (!comp)
6586 return ERROR_SUCCESS;
6588 comp->Action = msi_get_component_action( package, comp );
6589 if (comp->Action != INSTALLSTATE_ABSENT)
6591 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6592 return ERROR_SUCCESS;
6595 desc = MSI_RecordGetString( rec, 3 );
6596 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6598 WARN("Failed to remove ODBC driver\n");
6600 else if (!usage)
6602 FIXME("Usage count reached 0\n");
6605 uirow = MSI_CreateRecord( 2 );
6606 MSI_RecordSetStringW( uirow, 1, desc );
6607 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6608 msi_ui_actiondata( package, szRemoveODBC, uirow );
6609 msiobj_release( &uirow->hdr );
6611 return ERROR_SUCCESS;
6614 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6616 MSIPACKAGE *package = param;
6617 MSICOMPONENT *comp;
6618 MSIRECORD *uirow;
6619 DWORD usage;
6620 LPCWSTR desc, component;
6622 component = MSI_RecordGetString( rec, 2 );
6623 comp = msi_get_loaded_component( package, component );
6624 if (!comp)
6625 return ERROR_SUCCESS;
6627 comp->Action = msi_get_component_action( package, comp );
6628 if (comp->Action != INSTALLSTATE_ABSENT)
6630 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6631 return ERROR_SUCCESS;
6634 desc = MSI_RecordGetString( rec, 3 );
6635 if (!SQLRemoveTranslatorW( desc, &usage ))
6637 WARN("Failed to remove ODBC translator\n");
6639 else if (!usage)
6641 FIXME("Usage count reached 0\n");
6644 uirow = MSI_CreateRecord( 2 );
6645 MSI_RecordSetStringW( uirow, 1, desc );
6646 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6647 msi_ui_actiondata( package, szRemoveODBC, uirow );
6648 msiobj_release( &uirow->hdr );
6650 return ERROR_SUCCESS;
6653 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6655 MSIPACKAGE *package = param;
6656 MSICOMPONENT *comp;
6657 MSIRECORD *uirow;
6658 LPWSTR attrs;
6659 LPCWSTR desc, driver, component;
6660 WORD request = ODBC_REMOVE_SYS_DSN;
6661 INT registration;
6662 DWORD len;
6664 static const WCHAR attrs_fmt[] = {
6665 'D','S','N','=','%','s',0 };
6667 component = MSI_RecordGetString( rec, 2 );
6668 comp = msi_get_loaded_component( package, component );
6669 if (!comp)
6670 return ERROR_SUCCESS;
6672 comp->Action = msi_get_component_action( package, comp );
6673 if (comp->Action != INSTALLSTATE_ABSENT)
6675 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6676 return ERROR_SUCCESS;
6679 desc = MSI_RecordGetString( rec, 3 );
6680 driver = MSI_RecordGetString( rec, 4 );
6681 registration = MSI_RecordGetInteger( rec, 5 );
6683 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6684 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6686 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6687 attrs = msi_alloc( len * sizeof(WCHAR) );
6688 if (!attrs)
6689 return ERROR_OUTOFMEMORY;
6691 FIXME("Use ODBCSourceAttribute table\n");
6693 len = sprintfW( attrs, attrs_fmt, desc );
6694 attrs[len + 1] = 0;
6696 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6698 WARN("Failed to remove ODBC data source\n");
6700 msi_free( attrs );
6702 uirow = MSI_CreateRecord( 3 );
6703 MSI_RecordSetStringW( uirow, 1, desc );
6704 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6705 MSI_RecordSetInteger( uirow, 3, request );
6706 msi_ui_actiondata( package, szRemoveODBC, uirow );
6707 msiobj_release( &uirow->hdr );
6709 return ERROR_SUCCESS;
6712 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6714 static const WCHAR driver_query[] = {
6715 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6716 'O','D','B','C','D','r','i','v','e','r',0};
6717 static const WCHAR translator_query[] = {
6718 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6719 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6720 static const WCHAR source_query[] = {
6721 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6722 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6723 MSIQUERY *view;
6724 UINT rc;
6726 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6727 if (rc == ERROR_SUCCESS)
6729 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6730 msiobj_release( &view->hdr );
6731 if (rc != ERROR_SUCCESS)
6732 return rc;
6734 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6735 if (rc == ERROR_SUCCESS)
6737 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6738 msiobj_release( &view->hdr );
6739 if (rc != ERROR_SUCCESS)
6740 return rc;
6742 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6743 if (rc == ERROR_SUCCESS)
6745 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6746 msiobj_release( &view->hdr );
6747 if (rc != ERROR_SUCCESS)
6748 return rc;
6750 return ERROR_SUCCESS;
6753 #define ENV_ACT_SETALWAYS 0x1
6754 #define ENV_ACT_SETABSENT 0x2
6755 #define ENV_ACT_REMOVE 0x4
6756 #define ENV_ACT_REMOVEMATCH 0x8
6758 #define ENV_MOD_MACHINE 0x20000000
6759 #define ENV_MOD_APPEND 0x40000000
6760 #define ENV_MOD_PREFIX 0x80000000
6761 #define ENV_MOD_MASK 0xC0000000
6763 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6765 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6767 LPCWSTR cptr = *name;
6769 static const WCHAR prefix[] = {'[','~',']',0};
6770 static const int prefix_len = 3;
6772 *flags = 0;
6773 while (*cptr)
6775 if (*cptr == '=')
6776 *flags |= ENV_ACT_SETALWAYS;
6777 else if (*cptr == '+')
6778 *flags |= ENV_ACT_SETABSENT;
6779 else if (*cptr == '-')
6780 *flags |= ENV_ACT_REMOVE;
6781 else if (*cptr == '!')
6782 *flags |= ENV_ACT_REMOVEMATCH;
6783 else if (*cptr == '*')
6784 *flags |= ENV_MOD_MACHINE;
6785 else
6786 break;
6788 cptr++;
6789 (*name)++;
6792 if (!*cptr)
6794 ERR("Missing environment variable\n");
6795 return ERROR_FUNCTION_FAILED;
6798 if (*value)
6800 LPCWSTR ptr = *value;
6801 if (!strncmpW(ptr, prefix, prefix_len))
6803 if (ptr[prefix_len] == szSemiColon[0])
6805 *flags |= ENV_MOD_APPEND;
6806 *value += lstrlenW(prefix);
6808 else
6810 *value = NULL;
6813 else if (lstrlenW(*value) >= prefix_len)
6815 ptr += lstrlenW(ptr) - prefix_len;
6816 if (!strcmpW( ptr, prefix ))
6818 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6820 *flags |= ENV_MOD_PREFIX;
6821 /* the "[~]" will be removed by deformat_string */;
6823 else
6825 *value = NULL;
6831 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6832 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6833 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6834 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6836 ERR("Invalid flags: %08x\n", *flags);
6837 return ERROR_FUNCTION_FAILED;
6840 if (!*flags)
6841 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6843 return ERROR_SUCCESS;
6846 static UINT open_env_key( DWORD flags, HKEY *key )
6848 static const WCHAR user_env[] =
6849 {'E','n','v','i','r','o','n','m','e','n','t',0};
6850 static const WCHAR machine_env[] =
6851 {'S','y','s','t','e','m','\\',
6852 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6853 'C','o','n','t','r','o','l','\\',
6854 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6855 'E','n','v','i','r','o','n','m','e','n','t',0};
6856 const WCHAR *env;
6857 HKEY root;
6858 LONG res;
6860 if (flags & ENV_MOD_MACHINE)
6862 env = machine_env;
6863 root = HKEY_LOCAL_MACHINE;
6865 else
6867 env = user_env;
6868 root = HKEY_CURRENT_USER;
6871 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6872 if (res != ERROR_SUCCESS)
6874 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6875 return ERROR_FUNCTION_FAILED;
6878 return ERROR_SUCCESS;
6881 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6883 MSIPACKAGE *package = param;
6884 LPCWSTR name, value, component;
6885 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6886 DWORD flags, type, size;
6887 UINT res;
6888 HKEY env = NULL;
6889 MSICOMPONENT *comp;
6890 MSIRECORD *uirow;
6891 int action = 0;
6893 component = MSI_RecordGetString(rec, 4);
6894 comp = msi_get_loaded_component(package, component);
6895 if (!comp)
6896 return ERROR_SUCCESS;
6898 comp->Action = msi_get_component_action( package, comp );
6899 if (comp->Action != INSTALLSTATE_LOCAL)
6901 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6902 return ERROR_SUCCESS;
6904 name = MSI_RecordGetString(rec, 2);
6905 value = MSI_RecordGetString(rec, 3);
6907 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6909 res = env_parse_flags(&name, &value, &flags);
6910 if (res != ERROR_SUCCESS || !value)
6911 goto done;
6913 if (value && !deformat_string(package, value, &deformatted))
6915 res = ERROR_OUTOFMEMORY;
6916 goto done;
6919 value = deformatted;
6921 res = open_env_key( flags, &env );
6922 if (res != ERROR_SUCCESS)
6923 goto done;
6925 if (flags & ENV_MOD_MACHINE)
6926 action |= 0x20000000;
6928 size = 0;
6929 type = REG_SZ;
6930 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6931 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6932 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6933 goto done;
6935 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6937 action = 0x2;
6939 /* Nothing to do. */
6940 if (!value)
6942 res = ERROR_SUCCESS;
6943 goto done;
6946 /* If we are appending but the string was empty, strip ; */
6947 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6949 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6950 newval = strdupW(value);
6951 if (!newval)
6953 res = ERROR_OUTOFMEMORY;
6954 goto done;
6957 else
6959 action = 0x1;
6961 /* Contrary to MSDN, +-variable to [~];path works */
6962 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6964 res = ERROR_SUCCESS;
6965 goto done;
6968 data = msi_alloc(size);
6969 if (!data)
6971 RegCloseKey(env);
6972 return ERROR_OUTOFMEMORY;
6975 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6976 if (res != ERROR_SUCCESS)
6977 goto done;
6979 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6981 action = 0x4;
6982 res = RegDeleteValueW(env, name);
6983 if (res != ERROR_SUCCESS)
6984 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6985 goto done;
6988 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6989 if (flags & ENV_MOD_MASK)
6991 DWORD mod_size;
6992 int multiplier = 0;
6993 if (flags & ENV_MOD_APPEND) multiplier++;
6994 if (flags & ENV_MOD_PREFIX) multiplier++;
6995 mod_size = lstrlenW(value) * multiplier;
6996 size += mod_size * sizeof(WCHAR);
6999 newval = msi_alloc(size);
7000 ptr = newval;
7001 if (!newval)
7003 res = ERROR_OUTOFMEMORY;
7004 goto done;
7007 if (flags & ENV_MOD_PREFIX)
7009 lstrcpyW(newval, value);
7010 ptr = newval + lstrlenW(value);
7011 action |= 0x80000000;
7014 lstrcpyW(ptr, data);
7016 if (flags & ENV_MOD_APPEND)
7018 lstrcatW(newval, value);
7019 action |= 0x40000000;
7022 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7023 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7024 if (res)
7026 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7029 done:
7030 uirow = MSI_CreateRecord( 3 );
7031 MSI_RecordSetStringW( uirow, 1, name );
7032 MSI_RecordSetStringW( uirow, 2, newval );
7033 MSI_RecordSetInteger( uirow, 3, action );
7034 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7035 msiobj_release( &uirow->hdr );
7037 if (env) RegCloseKey(env);
7038 msi_free(deformatted);
7039 msi_free(data);
7040 msi_free(newval);
7041 return res;
7044 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7046 static const WCHAR query[] = {
7047 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7048 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7049 MSIQUERY *view;
7050 UINT rc;
7052 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7053 if (rc != ERROR_SUCCESS)
7054 return ERROR_SUCCESS;
7056 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7057 msiobj_release(&view->hdr);
7058 return rc;
7061 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7063 MSIPACKAGE *package = param;
7064 LPCWSTR name, value, component;
7065 LPWSTR deformatted = NULL;
7066 DWORD flags;
7067 HKEY env;
7068 MSICOMPONENT *comp;
7069 MSIRECORD *uirow;
7070 int action = 0;
7071 LONG res;
7072 UINT r;
7074 component = MSI_RecordGetString( rec, 4 );
7075 comp = msi_get_loaded_component( package, component );
7076 if (!comp)
7077 return ERROR_SUCCESS;
7079 comp->Action = msi_get_component_action( package, comp );
7080 if (comp->Action != INSTALLSTATE_ABSENT)
7082 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7083 return ERROR_SUCCESS;
7085 name = MSI_RecordGetString( rec, 2 );
7086 value = MSI_RecordGetString( rec, 3 );
7088 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7090 r = env_parse_flags( &name, &value, &flags );
7091 if (r != ERROR_SUCCESS)
7092 return r;
7094 if (!(flags & ENV_ACT_REMOVE))
7096 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7097 return ERROR_SUCCESS;
7100 if (value && !deformat_string( package, value, &deformatted ))
7101 return ERROR_OUTOFMEMORY;
7103 value = deformatted;
7105 r = open_env_key( flags, &env );
7106 if (r != ERROR_SUCCESS)
7108 r = ERROR_SUCCESS;
7109 goto done;
7112 if (flags & ENV_MOD_MACHINE)
7113 action |= 0x20000000;
7115 TRACE("Removing %s\n", debugstr_w(name));
7117 res = RegDeleteValueW( env, name );
7118 if (res != ERROR_SUCCESS)
7120 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7121 r = ERROR_SUCCESS;
7124 done:
7125 uirow = MSI_CreateRecord( 3 );
7126 MSI_RecordSetStringW( uirow, 1, name );
7127 MSI_RecordSetStringW( uirow, 2, value );
7128 MSI_RecordSetInteger( uirow, 3, action );
7129 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7130 msiobj_release( &uirow->hdr );
7132 if (env) RegCloseKey( env );
7133 msi_free( deformatted );
7134 return r;
7137 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7139 static const WCHAR query[] = {
7140 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7141 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7142 MSIQUERY *view;
7143 UINT rc;
7145 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7146 if (rc != ERROR_SUCCESS)
7147 return ERROR_SUCCESS;
7149 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7150 msiobj_release( &view->hdr );
7151 return rc;
7154 UINT msi_validate_product_id( MSIPACKAGE *package )
7156 LPWSTR key, template, id;
7157 UINT r = ERROR_SUCCESS;
7159 id = msi_dup_property( package->db, szProductID );
7160 if (id)
7162 msi_free( id );
7163 return ERROR_SUCCESS;
7165 template = msi_dup_property( package->db, szPIDTemplate );
7166 key = msi_dup_property( package->db, szPIDKEY );
7167 if (key && template)
7169 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7170 r = msi_set_property( package->db, szProductID, key, -1 );
7172 msi_free( template );
7173 msi_free( key );
7174 return r;
7177 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7179 return msi_validate_product_id( package );
7182 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7184 TRACE("\n");
7185 package->need_reboot_at_end = 1;
7186 return ERROR_SUCCESS;
7189 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7191 static const WCHAR szAvailableFreeReg[] =
7192 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7193 MSIRECORD *uirow;
7194 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7196 TRACE("%p %d kilobytes\n", package, space);
7198 uirow = MSI_CreateRecord( 1 );
7199 MSI_RecordSetInteger( uirow, 1, space );
7200 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7201 msiobj_release( &uirow->hdr );
7203 return ERROR_SUCCESS;
7206 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7208 TRACE("%p\n", package);
7210 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7211 return ERROR_SUCCESS;
7214 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7216 FIXME("%p\n", package);
7217 return ERROR_SUCCESS;
7220 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7222 static const WCHAR driver_query[] = {
7223 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7224 'O','D','B','C','D','r','i','v','e','r',0};
7225 static const WCHAR translator_query[] = {
7226 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7227 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7228 MSIQUERY *view;
7229 UINT r, count;
7231 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7232 if (r == ERROR_SUCCESS)
7234 count = 0;
7235 r = MSI_IterateRecords( view, &count, NULL, package );
7236 msiobj_release( &view->hdr );
7237 if (r != ERROR_SUCCESS)
7238 return r;
7239 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7241 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7242 if (r == ERROR_SUCCESS)
7244 count = 0;
7245 r = MSI_IterateRecords( view, &count, NULL, package );
7246 msiobj_release( &view->hdr );
7247 if (r != ERROR_SUCCESS)
7248 return r;
7249 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7251 return ERROR_SUCCESS;
7254 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7256 static const WCHAR fmtW[] =
7257 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7258 MSIPACKAGE *package = param;
7259 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7260 int attrs = MSI_RecordGetInteger( rec, 5 );
7261 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7262 WCHAR *product, *features, *cmd;
7263 STARTUPINFOW si;
7264 PROCESS_INFORMATION info;
7265 BOOL ret;
7267 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7268 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7270 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7272 len += strlenW( product );
7273 if (features)
7274 len += strlenW( features );
7275 else
7276 len += sizeof(szAll) / sizeof(szAll[0]);
7278 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7280 msi_free( product );
7281 msi_free( features );
7282 return ERROR_OUTOFMEMORY;
7284 sprintfW( cmd, fmtW, product, features ? features : szAll );
7285 msi_free( product );
7286 msi_free( features );
7288 memset( &si, 0, sizeof(STARTUPINFOW) );
7289 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7290 msi_free( cmd );
7291 if (!ret) return GetLastError();
7292 CloseHandle( info.hThread );
7294 WaitForSingleObject( info.hProcess, INFINITE );
7295 CloseHandle( info.hProcess );
7296 return ERROR_SUCCESS;
7299 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7301 static const WCHAR query[] = {
7302 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7303 MSIQUERY *view;
7304 UINT r;
7306 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7307 if (r == ERROR_SUCCESS)
7309 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7310 msiobj_release( &view->hdr );
7311 if (r != ERROR_SUCCESS)
7312 return r;
7314 return ERROR_SUCCESS;
7317 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7319 MSIPACKAGE *package = param;
7320 int attributes = MSI_RecordGetInteger( rec, 5 );
7322 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7324 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7325 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7326 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7327 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7328 HKEY hkey;
7329 UINT r;
7331 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7333 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7334 if (r != ERROR_SUCCESS)
7335 return ERROR_SUCCESS;
7337 else
7339 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7340 if (r != ERROR_SUCCESS)
7341 return ERROR_SUCCESS;
7343 RegCloseKey( hkey );
7345 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7346 debugstr_w(upgrade_code), debugstr_w(version_min),
7347 debugstr_w(version_max), debugstr_w(language));
7349 return ERROR_SUCCESS;
7352 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7354 static const WCHAR query[] = {
7355 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7356 'U','p','g','r','a','d','e',0};
7357 MSIQUERY *view;
7358 UINT r;
7360 if (msi_get_property_int( package->db, szInstalled, 0 ))
7362 TRACE("product is installed, skipping action\n");
7363 return ERROR_SUCCESS;
7365 if (msi_get_property_int( package->db, szPreselected, 0 ))
7367 TRACE("Preselected property is set, not migrating feature states\n");
7368 return ERROR_SUCCESS;
7370 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7371 if (r == ERROR_SUCCESS)
7373 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7374 msiobj_release( &view->hdr );
7375 if (r != ERROR_SUCCESS)
7376 return r;
7378 return ERROR_SUCCESS;
7381 static void bind_image( const char *filename, const char *path )
7383 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7385 WARN("failed to bind image %u\n", GetLastError());
7389 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7391 UINT i;
7392 MSIFILE *file;
7393 MSIPACKAGE *package = param;
7394 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7395 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7396 char *filenameA, *pathA;
7397 WCHAR *pathW, **path_list;
7399 if (!(file = msi_get_loaded_file( package, key )))
7401 WARN("file %s not found\n", debugstr_w(key));
7402 return ERROR_SUCCESS;
7404 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7405 path_list = msi_split_string( paths, ';' );
7406 if (!path_list) bind_image( filenameA, NULL );
7407 else
7409 for (i = 0; path_list[i] && path_list[i][0]; i++)
7411 deformat_string( package, path_list[i], &pathW );
7412 if ((pathA = strdupWtoA( pathW )))
7414 bind_image( filenameA, pathA );
7415 msi_free( pathA );
7417 msi_free( pathW );
7420 msi_free( path_list );
7421 msi_free( filenameA );
7422 return ERROR_SUCCESS;
7425 static UINT ACTION_BindImage( MSIPACKAGE *package )
7427 static const WCHAR query[] = {
7428 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7429 'B','i','n','d','I','m','a','g','e',0};
7430 MSIQUERY *view;
7431 UINT r;
7433 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7434 if (r == ERROR_SUCCESS)
7436 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7437 msiobj_release( &view->hdr );
7438 if (r != ERROR_SUCCESS)
7439 return r;
7441 return ERROR_SUCCESS;
7444 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7446 static const WCHAR query[] = {
7447 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7448 MSIQUERY *view;
7449 DWORD count = 0;
7450 UINT r;
7452 r = MSI_OpenQuery( package->db, &view, query, table );
7453 if (r == ERROR_SUCCESS)
7455 r = MSI_IterateRecords(view, &count, NULL, package);
7456 msiobj_release(&view->hdr);
7457 if (r != ERROR_SUCCESS)
7458 return r;
7460 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7461 return ERROR_SUCCESS;
7464 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7466 static const WCHAR table[] = {
7467 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7468 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7471 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7473 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7474 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7477 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7479 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7480 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7483 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7485 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7486 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7489 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7491 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7492 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7495 static const struct
7497 const WCHAR *action;
7498 UINT (*handler)(MSIPACKAGE *);
7499 const WCHAR *action_rollback;
7501 StandardActions[] =
7503 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7504 { szAppSearch, ACTION_AppSearch, NULL },
7505 { szBindImage, ACTION_BindImage, NULL },
7506 { szCCPSearch, ACTION_CCPSearch, NULL },
7507 { szCostFinalize, ACTION_CostFinalize, NULL },
7508 { szCostInitialize, ACTION_CostInitialize, NULL },
7509 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7510 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7511 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7512 { szDisableRollback, ACTION_DisableRollback, NULL },
7513 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7514 { szExecuteAction, ACTION_ExecuteAction, NULL },
7515 { szFileCost, ACTION_FileCost, NULL },
7516 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7517 { szForceReboot, ACTION_ForceReboot, NULL },
7518 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7519 { szInstallExecute, ACTION_InstallExecute, NULL },
7520 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7521 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7522 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7523 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7524 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7525 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7526 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7527 { szInstallValidate, ACTION_InstallValidate, NULL },
7528 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7529 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7530 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7531 { szMoveFiles, ACTION_MoveFiles, NULL },
7532 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7533 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7534 { szPatchFiles, ACTION_PatchFiles, NULL },
7535 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7536 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7537 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7538 { szPublishProduct, ACTION_PublishProduct, NULL },
7539 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7540 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7541 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7542 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7543 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7544 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7545 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7546 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7547 { szRegisterUser, ACTION_RegisterUser, NULL },
7548 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7549 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7550 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7551 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7552 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7553 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7554 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7555 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7556 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7557 { szResolveSource, ACTION_ResolveSource, NULL },
7558 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7559 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7560 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7561 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7562 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7563 { szStartServices, ACTION_StartServices, szStopServices },
7564 { szStopServices, ACTION_StopServices, szStartServices },
7565 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7566 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7567 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7568 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7569 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7570 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7571 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7572 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7573 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7574 { szValidateProductID, ACTION_ValidateProductID, NULL },
7575 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7576 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7577 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7578 { NULL, NULL, NULL }
7581 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7583 BOOL ret = FALSE;
7584 UINT i;
7586 i = 0;
7587 while (StandardActions[i].action != NULL)
7589 if (!strcmpW( StandardActions[i].action, action ))
7591 ui_actionstart( package, action );
7592 if (StandardActions[i].handler)
7594 ui_actioninfo( package, action, TRUE, 0 );
7595 *rc = StandardActions[i].handler( package );
7596 ui_actioninfo( package, action, FALSE, *rc );
7598 if (StandardActions[i].action_rollback && !package->need_rollback)
7600 TRACE("scheduling rollback action\n");
7601 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7604 else
7606 FIXME("unhandled standard action %s\n", debugstr_w(action));
7607 *rc = ERROR_SUCCESS;
7609 ret = TRUE;
7610 break;
7612 i++;
7614 return ret;
7617 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7619 UINT rc = ERROR_SUCCESS;
7620 BOOL handled;
7622 TRACE("Performing action (%s)\n", debugstr_w(action));
7624 handled = ACTION_HandleStandardAction(package, action, &rc);
7626 if (!handled)
7627 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7629 if (!handled)
7631 WARN("unhandled msi action %s\n", debugstr_w(action));
7632 rc = ERROR_FUNCTION_NOT_CALLED;
7635 return rc;
7638 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7640 UINT rc = ERROR_SUCCESS;
7641 BOOL handled = FALSE;
7643 TRACE("Performing action (%s)\n", debugstr_w(action));
7645 package->action_progress_increment = 0;
7646 handled = ACTION_HandleStandardAction(package, action, &rc);
7648 if (!handled)
7649 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7651 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7652 handled = TRUE;
7654 if (!handled)
7656 WARN("unhandled msi action %s\n", debugstr_w(action));
7657 rc = ERROR_FUNCTION_NOT_CALLED;
7660 return rc;
7663 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7665 UINT rc = ERROR_SUCCESS;
7666 MSIRECORD *row;
7668 static const WCHAR query[] =
7669 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7670 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7671 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7672 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7673 static const WCHAR ui_query[] =
7674 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7675 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7676 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7677 ' ', '=',' ','%','i',0};
7679 if (needs_ui_sequence(package))
7680 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7681 else
7682 row = MSI_QueryGetRecord(package->db, query, seq);
7684 if (row)
7686 LPCWSTR action, cond;
7688 TRACE("Running the actions\n");
7690 /* check conditions */
7691 cond = MSI_RecordGetString(row, 2);
7693 /* this is a hack to skip errors in the condition code */
7694 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7696 msiobj_release(&row->hdr);
7697 return ERROR_SUCCESS;
7700 action = MSI_RecordGetString(row, 1);
7701 if (!action)
7703 ERR("failed to fetch action\n");
7704 msiobj_release(&row->hdr);
7705 return ERROR_FUNCTION_FAILED;
7708 if (needs_ui_sequence(package))
7709 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7710 else
7711 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7713 msiobj_release(&row->hdr);
7716 return rc;
7719 /****************************************************
7720 * TOP level entry points
7721 *****************************************************/
7723 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7724 LPCWSTR szCommandLine )
7726 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7727 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7728 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7729 WCHAR *reinstall, *remove, *patch;
7730 BOOL ui_exists;
7731 UINT rc;
7733 msi_set_property( package->db, szAction, szInstall, -1 );
7735 package->script->InWhatSequence = SEQUENCE_INSTALL;
7737 if (szPackagePath)
7739 LPWSTR p, dir;
7740 LPCWSTR file;
7742 dir = strdupW(szPackagePath);
7743 p = strrchrW(dir, '\\');
7744 if (p)
7746 *(++p) = 0;
7747 file = szPackagePath + (p - dir);
7749 else
7751 msi_free(dir);
7752 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7753 GetCurrentDirectoryW(MAX_PATH, dir);
7754 lstrcatW(dir, szBackSlash);
7755 file = szPackagePath;
7758 msi_free( package->PackagePath );
7759 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7760 if (!package->PackagePath)
7762 msi_free(dir);
7763 return ERROR_OUTOFMEMORY;
7766 lstrcpyW(package->PackagePath, dir);
7767 lstrcatW(package->PackagePath, file);
7768 msi_free(dir);
7770 msi_set_sourcedir_props(package, FALSE);
7773 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7774 if (rc != ERROR_SUCCESS)
7775 return rc;
7777 msi_apply_transforms( package );
7778 msi_apply_patches( package );
7780 patch = msi_dup_property( package->db, szPatch );
7781 remove = msi_dup_property( package->db, szRemove );
7782 reinstall = msi_dup_property( package->db, szReinstall );
7783 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7785 TRACE("setting REINSTALL property to ALL\n");
7786 msi_set_property( package->db, szReinstall, szAll, -1 );
7787 package->full_reinstall = 1;
7790 /* properties may have been added by a transform */
7791 msi_clone_properties( package );
7792 msi_set_original_database_property( package->db, szPackagePath );
7794 msi_parse_command_line( package, szCommandLine, FALSE );
7795 msi_adjust_privilege_properties( package );
7796 msi_set_context( package );
7798 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7800 TRACE("disabling rollback\n");
7801 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7804 if (needs_ui_sequence( package))
7806 package->script->InWhatSequence |= SEQUENCE_UI;
7807 rc = ACTION_ProcessUISequence(package);
7808 ui_exists = ui_sequence_exists(package);
7809 if (rc == ERROR_SUCCESS || !ui_exists)
7811 package->script->InWhatSequence |= SEQUENCE_EXEC;
7812 rc = ACTION_ProcessExecSequence(package, ui_exists);
7815 else
7816 rc = ACTION_ProcessExecSequence(package, FALSE);
7818 /* process the ending type action */
7819 if (rc == ERROR_SUCCESS)
7820 ACTION_PerformActionSequence(package, -1);
7821 else if (rc == ERROR_INSTALL_USEREXIT)
7822 ACTION_PerformActionSequence(package, -2);
7823 else if (rc == ERROR_INSTALL_SUSPEND)
7824 ACTION_PerformActionSequence(package, -4);
7825 else /* failed */
7827 ACTION_PerformActionSequence(package, -3);
7828 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7830 package->need_rollback = TRUE;
7834 /* finish up running custom actions */
7835 ACTION_FinishCustomActions(package);
7837 if (package->need_rollback && !reinstall)
7839 WARN("installation failed, running rollback script\n");
7840 execute_script( package, SCRIPT_ROLLBACK );
7842 msi_free( reinstall );
7843 msi_free( remove );
7844 msi_free( patch );
7846 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7847 return ERROR_SUCCESS_REBOOT_REQUIRED;
7849 return rc;