netapi32: Added missing breaks (Coverity).
[wine/multimedia.git] / dlls / msi / action.c
blob2a0bf87cee676509644bf31c85ee81b4df5c16d0
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 LPWSTR source, check;
429 if (msi_get_property_int( package->db, szInstalled, 0 ))
431 HKEY hkey;
433 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
434 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
435 RegCloseKey( hkey );
437 else
439 LPWSTR p, db;
440 DWORD len;
442 db = msi_dup_property( package->db, szOriginalDatabase );
443 if (!db)
444 return ERROR_OUTOFMEMORY;
446 p = strrchrW( db, '\\' );
447 if (!p)
449 p = strrchrW( db, '/' );
450 if (!p)
452 msi_free(db);
453 return ERROR_SUCCESS;
457 len = p - db + 2;
458 source = msi_alloc( len * sizeof(WCHAR) );
459 lstrcpynW( source, db, len );
460 msi_free( db );
463 check = msi_dup_property( package->db, szSourceDir );
464 if (!check || replace)
466 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
467 if (r == ERROR_SUCCESS)
468 msi_reset_folders( package, TRUE );
470 msi_free( check );
472 check = msi_dup_property( package->db, szSOURCEDIR );
473 if (!check || replace)
474 msi_set_property( package->db, szSOURCEDIR, source, -1 );
476 msi_free( check );
477 msi_free( source );
479 return ERROR_SUCCESS;
482 static BOOL needs_ui_sequence(MSIPACKAGE *package)
484 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
487 UINT msi_set_context(MSIPACKAGE *package)
489 UINT r = msi_locate_product( package->ProductCode, &package->Context );
490 if (r != ERROR_SUCCESS)
492 int num = msi_get_property_int( package->db, szAllUsers, 0 );
493 if (num == 1 || num == 2)
494 package->Context = MSIINSTALLCONTEXT_MACHINE;
495 else
496 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
498 return ERROR_SUCCESS;
501 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
503 UINT rc;
504 LPCWSTR cond, action;
505 MSIPACKAGE *package = param;
507 action = MSI_RecordGetString(row,1);
508 if (!action)
510 ERR("Error is retrieving action name\n");
511 return ERROR_FUNCTION_FAILED;
514 /* check conditions */
515 cond = MSI_RecordGetString(row,2);
517 /* this is a hack to skip errors in the condition code */
518 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
520 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
521 return ERROR_SUCCESS;
524 if (needs_ui_sequence(package))
525 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
526 else
527 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
529 msi_dialog_check_messages( NULL );
531 if (package->CurrentInstallState != ERROR_SUCCESS)
532 rc = package->CurrentInstallState;
534 if (rc == ERROR_FUNCTION_NOT_CALLED)
535 rc = ERROR_SUCCESS;
537 if (rc != ERROR_SUCCESS)
538 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
540 if (package->need_reboot_now)
542 TRACE("action %s asked for immediate reboot, suspending installation\n",
543 debugstr_w(action));
544 rc = ACTION_ForceReboot( package );
546 return rc;
549 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
551 static const WCHAR query[] = {
552 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
553 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
554 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
555 '`','S','e','q','u','e','n','c','e','`',0};
556 MSIQUERY *view;
557 UINT r;
559 TRACE("%p %s\n", package, debugstr_w(table));
561 r = MSI_OpenQuery( package->db, &view, query, table );
562 if (r == ERROR_SUCCESS)
564 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
565 msiobj_release(&view->hdr);
567 return r;
570 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
572 static const WCHAR query[] = {
573 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
574 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
575 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
576 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
577 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
578 static const WCHAR query_validate[] = {
579 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
580 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
581 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
582 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
583 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
584 MSIQUERY *view;
585 INT seq = 0;
586 UINT rc;
588 if (package->script->ExecuteSequenceRun)
590 TRACE("Execute Sequence already Run\n");
591 return ERROR_SUCCESS;
594 package->script->ExecuteSequenceRun = TRUE;
596 /* get the sequence number */
597 if (UIran)
599 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
600 if (!row) return ERROR_FUNCTION_FAILED;
601 seq = MSI_RecordGetInteger(row,1);
602 msiobj_release(&row->hdr);
604 rc = MSI_OpenQuery(package->db, &view, query, seq);
605 if (rc == ERROR_SUCCESS)
607 TRACE("Running the actions\n");
609 msi_set_property( package->db, szSourceDir, NULL, -1 );
610 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611 msiobj_release(&view->hdr);
613 return rc;
616 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
618 static const WCHAR query[] = {
619 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
620 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
621 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
622 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
623 MSIQUERY *view;
624 UINT rc;
626 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
627 if (rc == ERROR_SUCCESS)
629 TRACE("Running the actions\n");
630 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
631 msiobj_release(&view->hdr);
633 return rc;
636 /********************************************************
637 * ACTION helper functions and functions that perform the actions
638 *******************************************************/
639 static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
641 BOOL ret=FALSE;
642 UINT arc;
644 arc = ACTION_CustomAction( package, action, script );
645 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
647 *rc = arc;
648 ret = TRUE;
650 return ret;
653 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
655 MSICOMPONENT *comp;
657 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
659 if (!strcmpW( Component, comp->Component )) return comp;
661 return NULL;
664 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
666 MSIFEATURE *feature;
668 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
670 if (!strcmpW( Feature, feature->Feature )) return feature;
672 return NULL;
675 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
677 MSIFILE *file;
679 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
681 if (!strcmpW( key, file->File )) return file;
683 return NULL;
686 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
688 MSIFOLDER *folder;
690 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
692 if (!strcmpW( dir, folder->Directory )) return folder;
694 return NULL;
698 * Recursively create all directories in the path.
699 * shamelessly stolen from setupapi/queue.c
701 BOOL msi_create_full_path( const WCHAR *path )
703 BOOL ret = TRUE;
704 WCHAR *new_path;
705 int len;
707 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
708 strcpyW( new_path, path );
710 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
711 new_path[len - 1] = 0;
713 while (!CreateDirectoryW( new_path, NULL ))
715 WCHAR *slash;
716 DWORD last_error = GetLastError();
717 if (last_error == ERROR_ALREADY_EXISTS) break;
718 if (last_error != ERROR_PATH_NOT_FOUND)
720 ret = FALSE;
721 break;
723 if (!(slash = strrchrW( new_path, '\\' )))
725 ret = FALSE;
726 break;
728 len = slash - new_path;
729 new_path[len] = 0;
730 if (!msi_create_full_path( new_path ))
732 ret = FALSE;
733 break;
735 new_path[len] = '\\';
737 msi_free( new_path );
738 return ret;
741 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
743 MSIRECORD *row;
745 row = MSI_CreateRecord( 4 );
746 MSI_RecordSetInteger( row, 1, a );
747 MSI_RecordSetInteger( row, 2, b );
748 MSI_RecordSetInteger( row, 3, c );
749 MSI_RecordSetInteger( row, 4, d );
750 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
751 msiobj_release( &row->hdr );
753 msi_dialog_check_messages( NULL );
756 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
758 static const WCHAR query[] =
759 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
760 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
761 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
762 WCHAR message[1024];
763 MSIRECORD *row = 0;
764 DWORD size;
766 if (!package->LastAction || strcmpW( package->LastAction, action ))
768 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
770 if (MSI_RecordIsNull( row, 3 ))
772 msiobj_release( &row->hdr );
773 return;
775 /* update the cached action format */
776 msi_free( package->ActionFormat );
777 package->ActionFormat = msi_dup_record_field( row, 3 );
778 msi_free( package->LastAction );
779 package->LastAction = strdupW( action );
780 msiobj_release( &row->hdr );
782 size = 1024;
783 MSI_RecordSetStringW( record, 0, package->ActionFormat );
784 MSI_FormatRecordW( package, record, message, &size );
785 row = MSI_CreateRecord( 1 );
786 MSI_RecordSetStringW( row, 1, message );
787 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
788 msiobj_release( &row->hdr );
791 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
793 if (!comp->Enabled)
795 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
796 return INSTALLSTATE_UNKNOWN;
798 if (package->need_rollback) return comp->Installed;
799 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
801 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
802 return INSTALLSTATE_UNKNOWN;
804 return comp->ActionRequest;
807 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
809 if (package->need_rollback) return feature->Installed;
810 return feature->ActionRequest;
813 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
815 MSIPACKAGE *package = param;
816 LPCWSTR dir, component, full_path;
817 MSIRECORD *uirow;
818 MSIFOLDER *folder;
819 MSICOMPONENT *comp;
821 component = MSI_RecordGetString(row, 2);
822 if (!component)
823 return ERROR_SUCCESS;
825 comp = msi_get_loaded_component(package, component);
826 if (!comp)
827 return ERROR_SUCCESS;
829 comp->Action = msi_get_component_action( package, comp );
830 if (comp->Action != INSTALLSTATE_LOCAL)
832 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
833 return ERROR_SUCCESS;
836 dir = MSI_RecordGetString(row,1);
837 if (!dir)
839 ERR("Unable to get folder id\n");
840 return ERROR_SUCCESS;
843 uirow = MSI_CreateRecord(1);
844 MSI_RecordSetStringW(uirow, 1, dir);
845 msi_ui_actiondata(package, szCreateFolders, uirow);
846 msiobj_release(&uirow->hdr);
848 full_path = msi_get_target_folder( package, dir );
849 if (!full_path)
851 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
852 return ERROR_SUCCESS;
854 TRACE("folder is %s\n", debugstr_w(full_path));
856 folder = msi_get_loaded_folder( package, dir );
857 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
858 folder->State = FOLDER_STATE_CREATED;
859 return ERROR_SUCCESS;
862 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
864 static const WCHAR query[] = {
865 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
866 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
867 MSIQUERY *view;
868 UINT rc;
870 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
871 if (rc != ERROR_SUCCESS)
872 return ERROR_SUCCESS;
874 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
875 msiobj_release(&view->hdr);
876 return rc;
879 static void remove_persistent_folder( MSIFOLDER *folder )
881 FolderList *fl;
883 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
885 remove_persistent_folder( fl->folder );
887 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
889 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
893 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
895 MSIPACKAGE *package = param;
896 LPCWSTR dir, component, full_path;
897 MSIRECORD *uirow;
898 MSIFOLDER *folder;
899 MSICOMPONENT *comp;
901 component = MSI_RecordGetString(row, 2);
902 if (!component)
903 return ERROR_SUCCESS;
905 comp = msi_get_loaded_component(package, component);
906 if (!comp)
907 return ERROR_SUCCESS;
909 comp->Action = msi_get_component_action( package, comp );
910 if (comp->Action != INSTALLSTATE_ABSENT)
912 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
913 return ERROR_SUCCESS;
916 dir = MSI_RecordGetString( row, 1 );
917 if (!dir)
919 ERR("Unable to get folder id\n");
920 return ERROR_SUCCESS;
923 full_path = msi_get_target_folder( package, dir );
924 if (!full_path)
926 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
927 return ERROR_SUCCESS;
929 TRACE("folder is %s\n", debugstr_w(full_path));
931 uirow = MSI_CreateRecord( 1 );
932 MSI_RecordSetStringW( uirow, 1, dir );
933 msi_ui_actiondata( package, szRemoveFolders, uirow );
934 msiobj_release( &uirow->hdr );
936 folder = msi_get_loaded_folder( package, dir );
937 remove_persistent_folder( folder );
938 return ERROR_SUCCESS;
941 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
943 static const WCHAR query[] = {
944 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
945 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
946 MSIQUERY *view;
947 UINT rc;
949 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
950 if (rc != ERROR_SUCCESS)
951 return ERROR_SUCCESS;
953 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
954 msiobj_release( &view->hdr );
955 return rc;
958 static UINT load_component( MSIRECORD *row, LPVOID param )
960 MSIPACKAGE *package = param;
961 MSICOMPONENT *comp;
963 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
964 if (!comp)
965 return ERROR_FUNCTION_FAILED;
967 list_add_tail( &package->components, &comp->entry );
969 /* fill in the data */
970 comp->Component = msi_dup_record_field( row, 1 );
972 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
974 comp->ComponentId = msi_dup_record_field( row, 2 );
975 comp->Directory = msi_dup_record_field( row, 3 );
976 comp->Attributes = MSI_RecordGetInteger(row,4);
977 comp->Condition = msi_dup_record_field( row, 5 );
978 comp->KeyPath = msi_dup_record_field( row, 6 );
980 comp->Installed = INSTALLSTATE_UNKNOWN;
981 comp->Action = INSTALLSTATE_UNKNOWN;
982 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
984 comp->assembly = msi_load_assembly( package, comp );
985 return ERROR_SUCCESS;
988 UINT msi_load_all_components( MSIPACKAGE *package )
990 static const WCHAR query[] = {
991 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
992 '`','C','o','m','p','o','n','e','n','t','`',0};
993 MSIQUERY *view;
994 UINT r;
996 if (!list_empty(&package->components))
997 return ERROR_SUCCESS;
999 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1000 if (r != ERROR_SUCCESS)
1001 return r;
1003 if (!msi_init_assembly_caches( package ))
1005 ERR("can't initialize assembly caches\n");
1006 msiobj_release( &view->hdr );
1007 return ERROR_FUNCTION_FAILED;
1010 r = MSI_IterateRecords(view, NULL, load_component, package);
1011 msiobj_release(&view->hdr);
1012 return r;
1015 typedef struct {
1016 MSIPACKAGE *package;
1017 MSIFEATURE *feature;
1018 } _ilfs;
1020 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1022 ComponentList *cl;
1024 cl = msi_alloc( sizeof (*cl) );
1025 if ( !cl )
1026 return ERROR_NOT_ENOUGH_MEMORY;
1027 cl->component = comp;
1028 list_add_tail( &feature->Components, &cl->entry );
1030 return ERROR_SUCCESS;
1033 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1035 FeatureList *fl;
1037 fl = msi_alloc( sizeof(*fl) );
1038 if ( !fl )
1039 return ERROR_NOT_ENOUGH_MEMORY;
1040 fl->feature = child;
1041 list_add_tail( &parent->Children, &fl->entry );
1043 return ERROR_SUCCESS;
1046 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1048 _ilfs* ilfs = param;
1049 LPCWSTR component;
1050 MSICOMPONENT *comp;
1052 component = MSI_RecordGetString(row,1);
1054 /* check to see if the component is already loaded */
1055 comp = msi_get_loaded_component( ilfs->package, component );
1056 if (!comp)
1058 WARN("ignoring unknown component %s\n", debugstr_w(component));
1059 return ERROR_SUCCESS;
1061 add_feature_component( ilfs->feature, comp );
1062 comp->Enabled = TRUE;
1064 return ERROR_SUCCESS;
1067 static UINT load_feature(MSIRECORD * row, LPVOID param)
1069 static const WCHAR query[] = {
1070 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1071 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1072 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1073 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1074 MSIPACKAGE *package = param;
1075 MSIFEATURE *feature;
1076 MSIQUERY *view;
1077 _ilfs ilfs;
1078 UINT rc;
1080 /* fill in the data */
1082 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1083 if (!feature)
1084 return ERROR_NOT_ENOUGH_MEMORY;
1086 list_init( &feature->Children );
1087 list_init( &feature->Components );
1089 feature->Feature = msi_dup_record_field( row, 1 );
1091 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1093 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1094 feature->Title = msi_dup_record_field( row, 3 );
1095 feature->Description = msi_dup_record_field( row, 4 );
1097 if (!MSI_RecordIsNull(row,5))
1098 feature->Display = MSI_RecordGetInteger(row,5);
1100 feature->Level= MSI_RecordGetInteger(row,6);
1101 feature->Directory = msi_dup_record_field( row, 7 );
1102 feature->Attributes = MSI_RecordGetInteger(row,8);
1104 feature->Installed = INSTALLSTATE_UNKNOWN;
1105 feature->Action = INSTALLSTATE_UNKNOWN;
1106 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1108 list_add_tail( &package->features, &feature->entry );
1110 /* load feature components */
1112 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1113 if (rc != ERROR_SUCCESS)
1114 return ERROR_SUCCESS;
1116 ilfs.package = package;
1117 ilfs.feature = feature;
1119 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1120 msiobj_release(&view->hdr);
1121 return rc;
1124 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1126 MSIPACKAGE *package = param;
1127 MSIFEATURE *parent, *child;
1129 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1130 if (!child)
1131 return ERROR_FUNCTION_FAILED;
1133 if (!child->Feature_Parent)
1134 return ERROR_SUCCESS;
1136 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1137 if (!parent)
1138 return ERROR_FUNCTION_FAILED;
1140 add_feature_child( parent, child );
1141 return ERROR_SUCCESS;
1144 UINT msi_load_all_features( MSIPACKAGE *package )
1146 static const WCHAR query[] = {
1147 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1148 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1149 '`','D','i','s','p','l','a','y','`',0};
1150 MSIQUERY *view;
1151 UINT r;
1153 if (!list_empty(&package->features))
1154 return ERROR_SUCCESS;
1156 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1157 if (r != ERROR_SUCCESS)
1158 return r;
1160 r = MSI_IterateRecords( view, NULL, load_feature, package );
1161 if (r != ERROR_SUCCESS)
1163 msiobj_release( &view->hdr );
1164 return r;
1166 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1167 msiobj_release( &view->hdr );
1168 return r;
1171 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1173 if (!p)
1174 return p;
1175 p = strchrW(p, ch);
1176 if (!p)
1177 return p;
1178 *p = 0;
1179 return p+1;
1182 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1184 static const WCHAR query[] = {
1185 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1186 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1187 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1188 MSIQUERY *view = NULL;
1189 MSIRECORD *row = NULL;
1190 UINT r;
1192 TRACE("%s\n", debugstr_w(file->File));
1194 r = MSI_OpenQuery(package->db, &view, query, file->File);
1195 if (r != ERROR_SUCCESS)
1196 goto done;
1198 r = MSI_ViewExecute(view, NULL);
1199 if (r != ERROR_SUCCESS)
1200 goto done;
1202 r = MSI_ViewFetch(view, &row);
1203 if (r != ERROR_SUCCESS)
1204 goto done;
1206 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1207 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1208 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1209 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1210 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1212 done:
1213 if (view) msiobj_release(&view->hdr);
1214 if (row) msiobj_release(&row->hdr);
1215 return r;
1218 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1220 MSIRECORD *row;
1221 static const WCHAR query[] = {
1222 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1223 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1224 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1226 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1227 if (!row)
1229 WARN("query failed\n");
1230 return ERROR_FUNCTION_FAILED;
1233 file->disk_id = MSI_RecordGetInteger( row, 1 );
1234 msiobj_release( &row->hdr );
1235 return ERROR_SUCCESS;
1238 static UINT load_file(MSIRECORD *row, LPVOID param)
1240 MSIPACKAGE* package = param;
1241 LPCWSTR component;
1242 MSIFILE *file;
1244 /* fill in the data */
1246 file = msi_alloc_zero( sizeof (MSIFILE) );
1247 if (!file)
1248 return ERROR_NOT_ENOUGH_MEMORY;
1250 file->File = msi_dup_record_field( row, 1 );
1252 component = MSI_RecordGetString( row, 2 );
1253 file->Component = msi_get_loaded_component( package, component );
1255 if (!file->Component)
1257 WARN("Component not found: %s\n", debugstr_w(component));
1258 msi_free(file->File);
1259 msi_free(file);
1260 return ERROR_SUCCESS;
1263 file->FileName = msi_dup_record_field( row, 3 );
1264 msi_reduce_to_long_filename( file->FileName );
1266 file->ShortName = msi_dup_record_field( row, 3 );
1267 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1269 file->FileSize = MSI_RecordGetInteger( row, 4 );
1270 file->Version = msi_dup_record_field( row, 5 );
1271 file->Language = msi_dup_record_field( row, 6 );
1272 file->Attributes = MSI_RecordGetInteger( row, 7 );
1273 file->Sequence = MSI_RecordGetInteger( row, 8 );
1275 file->state = msifs_invalid;
1277 /* if the compressed bits are not set in the file attributes,
1278 * then read the information from the package word count property
1280 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1282 file->IsCompressed = FALSE;
1284 else if (file->Attributes &
1285 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1287 file->IsCompressed = TRUE;
1289 else if (file->Attributes & msidbFileAttributesNoncompressed)
1291 file->IsCompressed = FALSE;
1293 else
1295 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1298 load_file_hash(package, file);
1299 load_file_disk_id(package, file);
1301 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1303 list_add_tail( &package->files, &file->entry );
1305 return ERROR_SUCCESS;
1308 static UINT load_all_files(MSIPACKAGE *package)
1310 static const WCHAR query[] = {
1311 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1312 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1313 '`','S','e','q','u','e','n','c','e','`', 0};
1314 MSIQUERY *view;
1315 UINT rc;
1317 if (!list_empty(&package->files))
1318 return ERROR_SUCCESS;
1320 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1321 if (rc != ERROR_SUCCESS)
1322 return ERROR_SUCCESS;
1324 rc = MSI_IterateRecords(view, NULL, load_file, package);
1325 msiobj_release(&view->hdr);
1326 return rc;
1329 static UINT load_media( MSIRECORD *row, LPVOID param )
1331 MSIPACKAGE *package = param;
1332 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1333 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1335 /* FIXME: load external cabinets and directory sources too */
1336 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1337 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1338 return ERROR_SUCCESS;
1341 static UINT load_all_media( MSIPACKAGE *package )
1343 static const WCHAR query[] = {
1344 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1345 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1346 '`','D','i','s','k','I','d','`',0};
1347 MSIQUERY *view;
1348 UINT r;
1350 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1351 if (r != ERROR_SUCCESS)
1352 return ERROR_SUCCESS;
1354 r = MSI_IterateRecords( view, NULL, load_media, package );
1355 msiobj_release( &view->hdr );
1356 return r;
1359 static UINT load_patch(MSIRECORD *row, LPVOID param)
1361 MSIPACKAGE *package = param;
1362 MSIFILEPATCH *patch;
1363 LPWSTR file_key;
1365 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1366 if (!patch)
1367 return ERROR_NOT_ENOUGH_MEMORY;
1369 file_key = msi_dup_record_field( row, 1 );
1370 patch->File = msi_get_loaded_file( package, file_key );
1371 msi_free(file_key);
1373 if( !patch->File )
1375 ERR("Failed to find target for patch in File table\n");
1376 msi_free(patch);
1377 return ERROR_FUNCTION_FAILED;
1380 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1382 /* FIXME: The database should be properly transformed */
1383 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1385 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1386 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1387 patch->IsApplied = FALSE;
1389 /* FIXME:
1390 * Header field - for patch validation.
1391 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1394 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1396 list_add_tail( &package->filepatches, &patch->entry );
1398 return ERROR_SUCCESS;
1401 static UINT load_all_patches(MSIPACKAGE *package)
1403 static const WCHAR query[] = {
1404 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1405 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1406 '`','S','e','q','u','e','n','c','e','`',0};
1407 MSIQUERY *view;
1408 UINT rc;
1410 if (!list_empty(&package->filepatches))
1411 return ERROR_SUCCESS;
1413 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1414 if (rc != ERROR_SUCCESS)
1415 return ERROR_SUCCESS;
1417 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1418 msiobj_release(&view->hdr);
1419 return rc;
1422 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1424 static const WCHAR query[] = {
1425 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1426 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1427 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1428 MSIQUERY *view;
1430 folder->persistent = FALSE;
1431 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1433 if (!MSI_ViewExecute( view, NULL ))
1435 MSIRECORD *rec;
1436 if (!MSI_ViewFetch( view, &rec ))
1438 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1439 folder->persistent = TRUE;
1440 msiobj_release( &rec->hdr );
1443 msiobj_release( &view->hdr );
1445 return ERROR_SUCCESS;
1448 static UINT load_folder( MSIRECORD *row, LPVOID param )
1450 MSIPACKAGE *package = param;
1451 static WCHAR szEmpty[] = { 0 };
1452 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1453 MSIFOLDER *folder;
1455 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1456 list_init( &folder->children );
1457 folder->Directory = msi_dup_record_field( row, 1 );
1458 folder->Parent = msi_dup_record_field( row, 2 );
1459 p = msi_dup_record_field(row, 3);
1461 TRACE("%s\n", debugstr_w(folder->Directory));
1463 /* split src and target dir */
1464 tgt_short = p;
1465 src_short = folder_split_path( p, ':' );
1467 /* split the long and short paths */
1468 tgt_long = folder_split_path( tgt_short, '|' );
1469 src_long = folder_split_path( src_short, '|' );
1471 /* check for no-op dirs */
1472 if (tgt_short && !strcmpW( szDot, tgt_short ))
1473 tgt_short = szEmpty;
1474 if (src_short && !strcmpW( szDot, src_short ))
1475 src_short = szEmpty;
1477 if (!tgt_long)
1478 tgt_long = tgt_short;
1480 if (!src_short) {
1481 src_short = tgt_short;
1482 src_long = tgt_long;
1485 if (!src_long)
1486 src_long = src_short;
1488 /* FIXME: use the target short path too */
1489 folder->TargetDefault = strdupW(tgt_long);
1490 folder->SourceShortPath = strdupW(src_short);
1491 folder->SourceLongPath = strdupW(src_long);
1492 msi_free(p);
1494 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1495 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1496 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1498 load_folder_persistence( package, folder );
1500 list_add_tail( &package->folders, &folder->entry );
1501 return ERROR_SUCCESS;
1504 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1506 FolderList *fl;
1508 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1509 fl->folder = child;
1510 list_add_tail( &parent->children, &fl->entry );
1511 return ERROR_SUCCESS;
1514 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1516 MSIPACKAGE *package = param;
1517 MSIFOLDER *parent, *child;
1519 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1520 return ERROR_FUNCTION_FAILED;
1522 if (!child->Parent) return ERROR_SUCCESS;
1524 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1525 return ERROR_FUNCTION_FAILED;
1527 return add_folder_child( parent, child );
1530 static UINT load_all_folders( MSIPACKAGE *package )
1532 static const WCHAR query[] = {
1533 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1534 '`','D','i','r','e','c','t','o','r','y','`',0};
1535 MSIQUERY *view;
1536 UINT r;
1538 if (!list_empty(&package->folders))
1539 return ERROR_SUCCESS;
1541 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1542 if (r != ERROR_SUCCESS)
1543 return r;
1545 r = MSI_IterateRecords( view, NULL, load_folder, package );
1546 if (r != ERROR_SUCCESS)
1548 msiobj_release( &view->hdr );
1549 return r;
1551 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1552 msiobj_release( &view->hdr );
1553 return r;
1556 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1558 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1559 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1561 load_all_folders( package );
1562 msi_load_all_components( package );
1563 msi_load_all_features( package );
1564 load_all_files( package );
1565 load_all_patches( package );
1566 load_all_media( package );
1568 return ERROR_SUCCESS;
1571 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1573 const WCHAR *action = package->script->Actions[script][index];
1574 ui_actionstart( package, action );
1575 TRACE("executing %s\n", debugstr_w(action));
1576 return ACTION_PerformAction( package, action, script );
1579 static UINT execute_script( MSIPACKAGE *package, UINT script )
1581 UINT i, rc = ERROR_SUCCESS;
1583 TRACE("executing script %u\n", script);
1585 if (!package->script)
1587 ERR("no script!\n");
1588 return ERROR_FUNCTION_FAILED;
1590 if (script == SCRIPT_ROLLBACK)
1592 for (i = package->script->ActionCount[script]; i > 0; i--)
1594 rc = execute_script_action( package, script, i - 1 );
1595 if (rc != ERROR_SUCCESS) break;
1598 else
1600 for (i = 0; i < package->script->ActionCount[script]; i++)
1602 rc = execute_script_action( package, script, i );
1603 if (rc != ERROR_SUCCESS) break;
1606 msi_free_action_script(package, script);
1607 return rc;
1610 static UINT ACTION_FileCost(MSIPACKAGE *package)
1612 return ERROR_SUCCESS;
1615 static void get_client_counts( MSIPACKAGE *package )
1617 MSICOMPONENT *comp;
1618 HKEY hkey;
1620 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1622 if (!comp->ComponentId) continue;
1624 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1625 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1627 comp->num_clients = 0;
1628 continue;
1630 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1631 NULL, NULL, NULL, NULL );
1632 RegCloseKey( hkey );
1636 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1638 MSICOMPONENT *comp;
1639 UINT r;
1641 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1643 if (!comp->ComponentId) continue;
1645 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1646 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1647 &comp->Installed );
1648 if (r == ERROR_SUCCESS) continue;
1650 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1651 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1652 &comp->Installed );
1653 if (r == ERROR_SUCCESS) continue;
1655 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1656 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1657 &comp->Installed );
1658 if (r == ERROR_SUCCESS) continue;
1660 comp->Installed = INSTALLSTATE_ABSENT;
1664 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1666 MSIFEATURE *feature;
1668 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1670 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1672 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1673 feature->Installed = INSTALLSTATE_ABSENT;
1674 else
1675 feature->Installed = state;
1679 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1681 return (feature->Level > 0 && feature->Level <= level);
1684 static BOOL process_state_property(MSIPACKAGE* package, int level,
1685 LPCWSTR property, INSTALLSTATE state)
1687 LPWSTR override;
1688 MSIFEATURE *feature;
1690 override = msi_dup_property( package->db, property );
1691 if (!override)
1692 return FALSE;
1694 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1696 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1697 continue;
1699 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1701 if (!strcmpiW( override, szAll ))
1703 if (feature->Installed != state)
1705 feature->Action = state;
1706 feature->ActionRequest = state;
1709 else
1711 LPWSTR ptr = override;
1712 LPWSTR ptr2 = strchrW(override,',');
1714 while (ptr)
1716 int len = ptr2 - ptr;
1718 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1719 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1721 if (feature->Installed != state)
1723 feature->Action = state;
1724 feature->ActionRequest = state;
1726 break;
1728 if (ptr2)
1730 ptr=ptr2+1;
1731 ptr2 = strchrW(ptr,',');
1733 else
1734 break;
1738 msi_free(override);
1739 return TRUE;
1742 static BOOL process_overrides( MSIPACKAGE *package, int level )
1744 static const WCHAR szAddLocal[] =
1745 {'A','D','D','L','O','C','A','L',0};
1746 static const WCHAR szAddSource[] =
1747 {'A','D','D','S','O','U','R','C','E',0};
1748 static const WCHAR szAdvertise[] =
1749 {'A','D','V','E','R','T','I','S','E',0};
1750 BOOL ret = FALSE;
1752 /* all these activation/deactivation things happen in order and things
1753 * later on the list override things earlier on the list.
1755 * 0 INSTALLLEVEL processing
1756 * 1 ADDLOCAL
1757 * 2 REMOVE
1758 * 3 ADDSOURCE
1759 * 4 ADDDEFAULT
1760 * 5 REINSTALL
1761 * 6 ADVERTISE
1762 * 7 COMPADDLOCAL
1763 * 8 COMPADDSOURCE
1764 * 9 FILEADDLOCAL
1765 * 10 FILEADDSOURCE
1766 * 11 FILEADDDEFAULT
1768 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1769 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1770 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1771 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1772 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1774 if (ret && !package->full_reinstall)
1775 msi_set_property( package->db, szPreselected, szOne, -1 );
1777 return ret;
1780 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1782 int level;
1783 MSICOMPONENT* component;
1784 MSIFEATURE *feature;
1786 TRACE("Checking Install Level\n");
1788 level = msi_get_property_int(package->db, szInstallLevel, 1);
1790 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1792 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1794 if (!is_feature_selected( feature, level )) continue;
1796 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1798 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1800 feature->Action = INSTALLSTATE_SOURCE;
1801 feature->ActionRequest = INSTALLSTATE_SOURCE;
1803 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1805 feature->Action = INSTALLSTATE_ADVERTISED;
1806 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1808 else
1810 feature->Action = INSTALLSTATE_LOCAL;
1811 feature->ActionRequest = INSTALLSTATE_LOCAL;
1815 /* disable child features of unselected parent or follow parent */
1816 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1818 FeatureList *fl;
1820 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1822 if (!is_feature_selected( feature, level ))
1824 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1825 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1827 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1829 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1830 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1831 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1832 fl->feature->Action = feature->Action;
1833 fl->feature->ActionRequest = feature->ActionRequest;
1838 else /* preselected */
1840 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1842 if (!is_feature_selected( feature, level )) continue;
1844 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1846 if (feature->Installed == INSTALLSTATE_ABSENT)
1848 feature->Action = INSTALLSTATE_UNKNOWN;
1849 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1851 else
1853 feature->Action = feature->Installed;
1854 feature->ActionRequest = feature->Installed;
1858 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1860 FeatureList *fl;
1862 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1864 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1865 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1867 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1868 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1869 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1870 fl->feature->Action = feature->Action;
1871 fl->feature->ActionRequest = feature->ActionRequest;
1877 /* now we want to set component state based based on feature state */
1878 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1880 ComponentList *cl;
1882 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1883 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1884 feature->ActionRequest, feature->Action);
1886 if (!is_feature_selected( feature, level )) continue;
1888 /* features with components that have compressed files are made local */
1889 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1891 if (cl->component->ForceLocalState &&
1892 feature->ActionRequest == INSTALLSTATE_SOURCE)
1894 feature->Action = INSTALLSTATE_LOCAL;
1895 feature->ActionRequest = INSTALLSTATE_LOCAL;
1896 break;
1900 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1902 component = cl->component;
1904 switch (feature->ActionRequest)
1906 case INSTALLSTATE_ABSENT:
1907 component->anyAbsent = 1;
1908 break;
1909 case INSTALLSTATE_ADVERTISED:
1910 component->hasAdvertiseFeature = 1;
1911 break;
1912 case INSTALLSTATE_SOURCE:
1913 component->hasSourceFeature = 1;
1914 break;
1915 case INSTALLSTATE_LOCAL:
1916 component->hasLocalFeature = 1;
1917 break;
1918 case INSTALLSTATE_DEFAULT:
1919 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1920 component->hasAdvertiseFeature = 1;
1921 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1922 component->hasSourceFeature = 1;
1923 else
1924 component->hasLocalFeature = 1;
1925 break;
1926 default:
1927 break;
1932 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1934 /* check if it's local or source */
1935 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1936 (component->hasLocalFeature || component->hasSourceFeature))
1938 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1939 !component->ForceLocalState)
1941 component->Action = INSTALLSTATE_SOURCE;
1942 component->ActionRequest = INSTALLSTATE_SOURCE;
1944 else
1946 component->Action = INSTALLSTATE_LOCAL;
1947 component->ActionRequest = INSTALLSTATE_LOCAL;
1949 continue;
1952 /* if any feature is local, the component must be local too */
1953 if (component->hasLocalFeature)
1955 component->Action = INSTALLSTATE_LOCAL;
1956 component->ActionRequest = INSTALLSTATE_LOCAL;
1957 continue;
1959 if (component->hasSourceFeature)
1961 component->Action = INSTALLSTATE_SOURCE;
1962 component->ActionRequest = INSTALLSTATE_SOURCE;
1963 continue;
1965 if (component->hasAdvertiseFeature)
1967 component->Action = INSTALLSTATE_ADVERTISED;
1968 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1969 continue;
1971 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1972 if (component->anyAbsent && component->ComponentId)
1974 component->Action = INSTALLSTATE_ABSENT;
1975 component->ActionRequest = INSTALLSTATE_ABSENT;
1979 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1981 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1983 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1984 component->Action = INSTALLSTATE_LOCAL;
1985 component->ActionRequest = INSTALLSTATE_LOCAL;
1988 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1989 component->Installed == INSTALLSTATE_SOURCE &&
1990 component->hasSourceFeature)
1992 component->Action = INSTALLSTATE_UNKNOWN;
1993 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1996 TRACE("component %s (installed %d request %d action %d)\n",
1997 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1999 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
2000 component->num_clients++;
2001 else if (component->Action == INSTALLSTATE_ABSENT)
2002 component->num_clients--;
2005 return ERROR_SUCCESS;
2008 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2010 MSIPACKAGE *package = param;
2011 LPCWSTR name;
2012 MSIFEATURE *feature;
2014 name = MSI_RecordGetString( row, 1 );
2016 feature = msi_get_loaded_feature( package, name );
2017 if (!feature)
2018 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2019 else
2021 LPCWSTR Condition;
2022 Condition = MSI_RecordGetString(row,3);
2024 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2026 int level = MSI_RecordGetInteger(row,2);
2027 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2028 feature->Level = level;
2031 return ERROR_SUCCESS;
2034 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2036 static const WCHAR name[] = {'\\',0};
2037 VS_FIXEDFILEINFO *ptr, *ret;
2038 LPVOID version;
2039 DWORD versize, handle;
2040 UINT sz;
2042 versize = GetFileVersionInfoSizeW( filename, &handle );
2043 if (!versize)
2044 return NULL;
2046 version = msi_alloc( versize );
2047 if (!version)
2048 return NULL;
2050 GetFileVersionInfoW( filename, 0, versize, version );
2052 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2054 msi_free( version );
2055 return NULL;
2058 ret = msi_alloc( sz );
2059 memcpy( ret, ptr, sz );
2061 msi_free( version );
2062 return ret;
2065 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2067 DWORD ms, ls;
2069 msi_parse_version_string( version, &ms, &ls );
2071 if (fi->dwFileVersionMS > ms) return 1;
2072 else if (fi->dwFileVersionMS < ms) return -1;
2073 else if (fi->dwFileVersionLS > ls) return 1;
2074 else if (fi->dwFileVersionLS < ls) return -1;
2075 return 0;
2078 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2080 DWORD ms1, ms2;
2082 msi_parse_version_string( ver1, &ms1, NULL );
2083 msi_parse_version_string( ver2, &ms2, NULL );
2085 if (ms1 > ms2) return 1;
2086 else if (ms1 < ms2) return -1;
2087 return 0;
2090 DWORD msi_get_disk_file_size( LPCWSTR filename )
2092 HANDLE file;
2093 DWORD size;
2095 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2096 if (file == INVALID_HANDLE_VALUE)
2097 return INVALID_FILE_SIZE;
2099 size = GetFileSize( file, NULL );
2100 TRACE("size is %u\n", size);
2101 CloseHandle( file );
2102 return size;
2105 BOOL msi_file_hash_matches( MSIFILE *file )
2107 UINT r;
2108 MSIFILEHASHINFO hash;
2110 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2111 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2112 if (r != ERROR_SUCCESS)
2113 return FALSE;
2115 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2118 static WCHAR *get_temp_dir( void )
2120 static UINT id;
2121 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2123 GetTempPathW( MAX_PATH, tmp );
2124 for (;;)
2126 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2127 if (CreateDirectoryW( dir, NULL )) break;
2129 return strdupW( dir );
2133 * msi_build_directory_name()
2135 * This function is to save messing round with directory names
2136 * It handles adding backslashes between path segments,
2137 * and can add \ at the end of the directory name if told to.
2139 * It takes a variable number of arguments.
2140 * It always allocates a new string for the result, so make sure
2141 * to free the return value when finished with it.
2143 * The first arg is the number of path segments that follow.
2144 * The arguments following count are a list of path segments.
2145 * A path segment may be NULL.
2147 * Path segments will be added with a \ separating them.
2148 * A \ will not be added after the last segment, however if the
2149 * last segment is NULL, then the last character will be a \
2151 WCHAR *msi_build_directory_name( DWORD count, ... )
2153 DWORD sz = 1, i;
2154 WCHAR *dir;
2155 va_list va;
2157 va_start( va, count );
2158 for (i = 0; i < count; i++)
2160 const WCHAR *str = va_arg( va, const WCHAR * );
2161 if (str) sz += strlenW( str ) + 1;
2163 va_end( va );
2165 dir = msi_alloc( sz * sizeof(WCHAR) );
2166 dir[0] = 0;
2168 va_start( va, count );
2169 for (i = 0; i < count; i++)
2171 const WCHAR *str = va_arg( va, const WCHAR * );
2172 if (!str) continue;
2173 strcatW( dir, str );
2174 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2176 va_end( va );
2177 return dir;
2180 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2182 MSIASSEMBLY *assembly = file->Component->assembly;
2184 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2186 msi_free( file->TargetPath );
2187 if (assembly && !assembly->application)
2189 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2190 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2191 msi_track_tempfile( package, file->TargetPath );
2193 else
2195 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2196 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2199 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2202 static UINT calculate_file_cost( MSIPACKAGE *package )
2204 VS_FIXEDFILEINFO *file_version;
2205 WCHAR *font_version;
2206 MSIFILE *file;
2208 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2210 MSICOMPONENT *comp = file->Component;
2211 DWORD file_size;
2213 if (!comp->Enabled) continue;
2215 if (file->IsCompressed)
2216 comp->ForceLocalState = TRUE;
2218 set_target_path( package, file );
2220 if ((comp->assembly && !comp->assembly->installed) ||
2221 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2223 comp->Cost += file->FileSize;
2224 continue;
2226 file_size = msi_get_disk_file_size( file->TargetPath );
2228 if (file->Version)
2230 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2232 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2234 comp->Cost += file->FileSize - file_size;
2236 msi_free( file_version );
2237 continue;
2239 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2241 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2243 comp->Cost += file->FileSize - file_size;
2245 msi_free( font_version );
2246 continue;
2249 if (file_size != file->FileSize)
2251 comp->Cost += file->FileSize - file_size;
2254 return ERROR_SUCCESS;
2257 WCHAR *msi_normalize_path( const WCHAR *in )
2259 const WCHAR *p = in;
2260 WCHAR *q, *ret;
2261 int n, len = strlenW( in ) + 2;
2263 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2265 len = 0;
2266 while (1)
2268 /* copy until the end of the string or a space */
2269 while (*p != ' ' && (*q = *p))
2271 p++, len++;
2272 /* reduce many backslashes to one */
2273 if (*p != '\\' || *q != '\\')
2274 q++;
2277 /* quit at the end of the string */
2278 if (!*p)
2279 break;
2281 /* count the number of spaces */
2282 n = 0;
2283 while (p[n] == ' ')
2284 n++;
2286 /* if it's leading or trailing space, skip it */
2287 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2288 p += n;
2289 else /* copy n spaces */
2290 while (n && (*q++ = *p++)) n--;
2292 while (q - ret > 0 && q[-1] == ' ') q--;
2293 if (q - ret > 0 && q[-1] != '\\')
2295 q[0] = '\\';
2296 q[1] = 0;
2298 return ret;
2301 static WCHAR *get_install_location( MSIPACKAGE *package )
2303 HKEY hkey;
2304 WCHAR *path;
2306 if (!package->ProductCode) return NULL;
2307 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2308 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2310 msi_free( path );
2311 path = NULL;
2313 RegCloseKey( hkey );
2314 return path;
2317 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2319 FolderList *fl;
2320 MSIFOLDER *folder, *parent, *child;
2321 WCHAR *path, *normalized_path;
2323 TRACE("resolving %s\n", debugstr_w(name));
2325 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2327 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2329 if (!(path = get_install_location( package )) &&
2330 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2332 path = msi_dup_property( package->db, szRootDrive );
2335 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2337 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2339 parent = msi_get_loaded_folder( package, folder->Parent );
2340 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2342 else
2343 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2345 normalized_path = msi_normalize_path( path );
2346 msi_free( path );
2347 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2349 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2350 msi_free( normalized_path );
2351 return;
2353 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2354 msi_free( folder->ResolvedTarget );
2355 folder->ResolvedTarget = normalized_path;
2357 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2359 child = fl->folder;
2360 msi_resolve_target_folder( package, child->Directory, load_prop );
2362 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2365 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2367 static const WCHAR query[] =
2368 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2369 '`','C','o','n','d','i','t','i','o','n','`',0};
2370 static const WCHAR szOutOfDiskSpace[] =
2371 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2372 static const WCHAR szPrimaryFolder[] =
2373 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2374 static const WCHAR szPrimaryVolumePath[] =
2375 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2376 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2377 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2378 'A','v','a','i','l','a','b','l','e',0};
2379 MSICOMPONENT *comp;
2380 MSIQUERY *view;
2381 WCHAR *level, *primary_key, *primary_folder;
2382 UINT rc;
2384 TRACE("Building directory properties\n");
2385 msi_resolve_target_folder( package, szTargetDir, TRUE );
2387 TRACE("Evaluating component conditions\n");
2388 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2390 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2392 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2393 comp->Enabled = FALSE;
2395 else
2396 comp->Enabled = TRUE;
2398 get_client_counts( package );
2400 /* read components states from the registry */
2401 ACTION_GetComponentInstallStates(package);
2402 ACTION_GetFeatureInstallStates(package);
2404 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2406 TRACE("Evaluating feature conditions\n");
2408 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2409 if (rc == ERROR_SUCCESS)
2411 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2412 msiobj_release( &view->hdr );
2413 if (rc != ERROR_SUCCESS)
2414 return rc;
2418 TRACE("Calculating file cost\n");
2419 calculate_file_cost( package );
2421 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2422 /* set default run level if not set */
2423 level = msi_dup_property( package->db, szInstallLevel );
2424 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2425 msi_free(level);
2427 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2429 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2431 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2432 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2434 ULARGE_INTEGER free;
2436 primary_folder[2] = 0;
2437 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2439 static const WCHAR fmtW[] = {'%','l','u',0};
2440 WCHAR buf[21];
2442 sprintfW( buf, fmtW, free.QuadPart / 512 );
2443 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2445 toupperW( primary_folder[0] );
2446 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2448 msi_free( primary_folder );
2450 msi_free( primary_key );
2453 /* FIXME: check volume disk space */
2454 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2456 return MSI_SetFeatureStates(package);
2459 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size )
2461 BYTE *data = NULL;
2463 if (!value)
2465 *size = sizeof(WCHAR);
2466 *type = REG_SZ;
2467 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2468 return data;
2470 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2472 if (value[1]=='x')
2474 LPWSTR ptr;
2475 CHAR byte[5];
2476 LPWSTR deformated = NULL;
2477 int count;
2479 deformat_string(package, &value[2], &deformated);
2481 /* binary value type */
2482 ptr = deformated;
2483 *type = REG_BINARY;
2484 if (strlenW(ptr)%2)
2485 *size = (strlenW(ptr)/2)+1;
2486 else
2487 *size = strlenW(ptr)/2;
2489 data = msi_alloc(*size);
2491 byte[0] = '0';
2492 byte[1] = 'x';
2493 byte[4] = 0;
2494 count = 0;
2495 /* if uneven pad with a zero in front */
2496 if (strlenW(ptr)%2)
2498 byte[2]= '0';
2499 byte[3]= *ptr;
2500 ptr++;
2501 data[count] = (BYTE)strtol(byte,NULL,0);
2502 count ++;
2503 TRACE("Uneven byte count\n");
2505 while (*ptr)
2507 byte[2]= *ptr;
2508 ptr++;
2509 byte[3]= *ptr;
2510 ptr++;
2511 data[count] = (BYTE)strtol(byte,NULL,0);
2512 count ++;
2514 msi_free(deformated);
2516 TRACE("Data %i bytes(%i)\n",*size,count);
2518 else
2520 LPWSTR deformated;
2521 LPWSTR p;
2522 DWORD d = 0;
2523 deformat_string(package, &value[1], &deformated);
2525 *type=REG_DWORD;
2526 *size = sizeof(DWORD);
2527 data = msi_alloc(*size);
2528 p = deformated;
2529 if (*p == '-')
2530 p++;
2531 while (*p)
2533 if ( (*p < '0') || (*p > '9') )
2534 break;
2535 d *= 10;
2536 d += (*p - '0');
2537 p++;
2539 if (deformated[0] == '-')
2540 d = -d;
2541 *(LPDWORD)data = d;
2542 TRACE("DWORD %i\n",*(LPDWORD)data);
2544 msi_free(deformated);
2547 else
2549 const WCHAR *ptr = value;
2550 DWORD len;
2552 *type = REG_SZ;
2553 if (value[0] == '#')
2555 ptr++;
2556 if (value[1] == '%')
2558 ptr++;
2559 *type = REG_EXPAND_SZ;
2562 len = deformat_string( package, ptr, (WCHAR **)&data );
2563 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2564 *size = (len + 1) * sizeof(WCHAR);
2566 return data;
2569 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2571 const WCHAR *ret;
2573 switch (root)
2575 case -1:
2576 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2578 *root_key = HKEY_LOCAL_MACHINE;
2579 ret = szHLM;
2581 else
2583 *root_key = HKEY_CURRENT_USER;
2584 ret = szHCU;
2586 break;
2587 case 0:
2588 *root_key = HKEY_CLASSES_ROOT;
2589 ret = szHCR;
2590 break;
2591 case 1:
2592 *root_key = HKEY_CURRENT_USER;
2593 ret = szHCU;
2594 break;
2595 case 2:
2596 *root_key = HKEY_LOCAL_MACHINE;
2597 ret = szHLM;
2598 break;
2599 case 3:
2600 *root_key = HKEY_USERS;
2601 ret = szHU;
2602 break;
2603 default:
2604 ERR("Unknown root %i\n", root);
2605 return NULL;
2608 return ret;
2611 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2613 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2614 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2616 if ((is_64bit || is_wow64) &&
2617 !(comp->Attributes & msidbComponentAttributes64bit) &&
2618 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2620 UINT size;
2621 WCHAR *path_32node;
2623 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2624 if (!(path_32node = msi_alloc( size ))) return NULL;
2626 memcpy( path_32node, path, len * sizeof(WCHAR) );
2627 strcpyW( path_32node + len, szWow6432Node );
2628 strcatW( path_32node, szBackSlash );
2629 strcatW( path_32node, path + len );
2630 return path_32node;
2632 return strdupW( path );
2635 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2637 REGSAM access = KEY_ALL_ACCESS;
2638 WCHAR *subkey, *p, *q;
2639 HKEY hkey, ret = NULL;
2640 LONG res;
2642 if (is_wow64) access |= KEY_WOW64_64KEY;
2644 if (!(subkey = strdupW( path ))) return NULL;
2645 p = subkey;
2646 if ((q = strchrW( p, '\\' ))) *q = 0;
2647 if (create)
2648 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2649 else
2650 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2651 if (res)
2653 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2654 msi_free( subkey );
2655 return NULL;
2657 if (q && q[1])
2659 ret = open_key( hkey, q + 1, create );
2660 RegCloseKey( hkey );
2662 else ret = hkey;
2663 msi_free( subkey );
2664 return ret;
2667 static BOOL is_special_entry( const WCHAR *name )
2669 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2672 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2674 const WCHAR *p = str;
2675 WCHAR **ret;
2676 int i = 0;
2678 *count = 0;
2679 if (!str) return NULL;
2680 while ((p - str) < len)
2682 p += strlenW( p ) + 1;
2683 (*count)++;
2685 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2686 p = str;
2687 while ((p - str) < len)
2689 if (!(ret[i] = strdupW( p )))
2691 for (; i >= 0; i--) msi_free( ret[i] );
2692 msi_free( ret );
2693 return NULL;
2695 p += strlenW( p ) + 1;
2696 i++;
2698 return ret;
2701 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2702 WCHAR **right, DWORD right_count, DWORD *size )
2704 WCHAR *ret, *p;
2705 unsigned int i;
2707 *size = sizeof(WCHAR);
2708 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2709 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2711 if (!(ret = p = msi_alloc( *size ))) return NULL;
2713 for (i = 0; i < left_count; i++)
2715 strcpyW( p, left[i] );
2716 p += strlenW( p ) + 1;
2718 for (i = 0; i < right_count; i++)
2720 strcpyW( p, right[i] );
2721 p += strlenW( p ) + 1;
2723 *p = 0;
2724 return ret;
2727 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2728 WCHAR **new, DWORD new_count )
2730 DWORD ret = old_count;
2731 unsigned int i, j, k;
2733 for (i = 0; i < new_count; i++)
2735 for (j = 0; j < old_count; j++)
2737 if (old[j] && !strcmpW( new[i], old[j] ))
2739 msi_free( old[j] );
2740 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2741 old[k] = NULL;
2742 ret--;
2746 return ret;
2749 enum join_op
2751 JOIN_OP_APPEND,
2752 JOIN_OP_PREPEND,
2753 JOIN_OP_REPLACE
2756 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2757 WCHAR **new, DWORD new_count, DWORD *size )
2759 switch (op)
2761 case JOIN_OP_APPEND:
2762 old_count = remove_duplicate_values( old, old_count, new, new_count );
2763 return flatten_multi_string_values( old, old_count, new, new_count, size );
2765 case JOIN_OP_PREPEND:
2766 old_count = remove_duplicate_values( old, old_count, new, new_count );
2767 return flatten_multi_string_values( new, new_count, old, old_count, size );
2769 case JOIN_OP_REPLACE:
2770 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2772 default:
2773 ERR("unhandled join op %u\n", op);
2774 return NULL;
2778 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2779 BYTE *new_value, DWORD new_size, DWORD *size )
2781 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2782 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2783 enum join_op op = JOIN_OP_REPLACE;
2784 WCHAR **old = NULL, **new = NULL;
2785 BYTE *ret;
2787 if (new_size / sizeof(WCHAR) - 1 > 1)
2789 new_ptr = (const WCHAR *)new_value;
2790 new_len = new_size / sizeof(WCHAR) - 1;
2792 if (!new_ptr[0] && new_ptr[new_len - 1])
2794 op = JOIN_OP_APPEND;
2795 new_len--;
2796 new_ptr++;
2798 else if (new_ptr[0] && !new_ptr[new_len - 1])
2800 op = JOIN_OP_PREPEND;
2801 new_len--;
2803 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2805 op = JOIN_OP_REPLACE;
2806 new_len -= 2;
2807 new_ptr++;
2809 new = split_multi_string_values( new_ptr, new_len, &new_count );
2811 if (old_size / sizeof(WCHAR) - 1 > 1)
2813 old_ptr = (const WCHAR *)old_value;
2814 old_len = old_size / sizeof(WCHAR) - 1;
2815 old = split_multi_string_values( old_ptr, old_len, &old_count );
2817 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2818 for (i = 0; i < old_count; i++) msi_free( old[i] );
2819 for (i = 0; i < new_count; i++) msi_free( new[i] );
2820 msi_free( old );
2821 msi_free( new );
2822 return ret;
2825 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2827 BYTE *ret;
2828 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2829 if (!(ret = msi_alloc( *size ))) return NULL;
2830 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2831 return ret;
2834 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2836 MSIPACKAGE *package = param;
2837 BYTE *new_value, *old_value = NULL;
2838 HKEY root_key, hkey;
2839 DWORD type, old_type, new_size, old_size = 0;
2840 LPWSTR deformated, uikey, keypath;
2841 const WCHAR *szRoot, *component, *name, *key, *str;
2842 MSICOMPONENT *comp;
2843 MSIRECORD * uirow;
2844 INT root;
2845 BOOL check_first = FALSE;
2846 int len;
2848 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2850 component = MSI_RecordGetString(row, 6);
2851 comp = msi_get_loaded_component(package,component);
2852 if (!comp)
2853 return ERROR_SUCCESS;
2855 comp->Action = msi_get_component_action( package, comp );
2856 if (comp->Action != INSTALLSTATE_LOCAL)
2858 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2859 return ERROR_SUCCESS;
2862 name = MSI_RecordGetString(row, 4);
2863 if( MSI_RecordIsNull(row,5) && name )
2865 /* null values can have special meanings */
2866 if (name[0]=='-' && name[1] == 0)
2867 return ERROR_SUCCESS;
2868 if ((name[0] == '+' || name[0] == '*') && !name[1])
2869 check_first = TRUE;
2872 root = MSI_RecordGetInteger(row,2);
2873 key = MSI_RecordGetString(row, 3);
2875 szRoot = get_root_key( package, root, &root_key );
2876 if (!szRoot)
2877 return ERROR_SUCCESS;
2879 deformat_string(package, key , &deformated);
2880 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2881 strcpyW(uikey,szRoot);
2882 strcatW(uikey,deformated);
2884 keypath = get_keypath( comp, root_key, deformated );
2885 msi_free( deformated );
2886 if (!(hkey = open_key( root_key, keypath, TRUE )))
2888 ERR("Could not create key %s\n", debugstr_w(keypath));
2889 msi_free(uikey);
2890 msi_free(keypath);
2891 return ERROR_FUNCTION_FAILED;
2893 str = msi_record_get_string( row, 5, &len );
2894 if (str && len > strlenW( str ))
2896 type = REG_MULTI_SZ;
2897 new_size = (len + 1) * sizeof(WCHAR);
2898 new_value = (BYTE *)msi_strdupW( str, len );
2900 else new_value = parse_value( package, str, &type, &new_size );
2901 deformat_string(package, name, &deformated);
2903 if (!is_special_entry( name ))
2905 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2906 if (type == REG_MULTI_SZ)
2908 BYTE *new;
2909 if (old_value && old_type != REG_MULTI_SZ)
2911 msi_free( old_value );
2912 old_value = NULL;
2913 old_size = 0;
2915 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2916 msi_free( new_value );
2917 new_value = new;
2919 if (!check_first)
2921 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2922 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2924 else if (!old_value)
2926 if (deformated || new_size)
2928 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2929 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2932 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2934 RegCloseKey(hkey);
2936 uirow = MSI_CreateRecord(3);
2937 MSI_RecordSetStringW(uirow,2,deformated);
2938 MSI_RecordSetStringW(uirow,1,uikey);
2939 if (type == REG_SZ || type == REG_EXPAND_SZ)
2940 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2941 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2942 msiobj_release( &uirow->hdr );
2944 msi_free(new_value);
2945 msi_free(old_value);
2946 msi_free(deformated);
2947 msi_free(uikey);
2948 msi_free(keypath);
2950 return ERROR_SUCCESS;
2953 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2955 static const WCHAR query[] = {
2956 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2957 '`','R','e','g','i','s','t','r','y','`',0};
2958 MSIQUERY *view;
2959 UINT rc;
2961 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2962 if (rc != ERROR_SUCCESS)
2963 return ERROR_SUCCESS;
2965 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2966 msiobj_release(&view->hdr);
2967 return rc;
2970 static void delete_key( HKEY root, const WCHAR *path )
2972 REGSAM access = 0;
2973 WCHAR *subkey, *p;
2974 HKEY hkey;
2975 LONG res;
2977 if (is_wow64) access |= KEY_WOW64_64KEY;
2979 if (!(subkey = strdupW( path ))) return;
2980 for (;;)
2982 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2983 hkey = open_key( root, subkey, FALSE );
2984 if (!hkey) break;
2985 if (p && p[1])
2986 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2987 else
2988 res = RegDeleteKeyExW( root, subkey, access, 0 );
2989 if (res)
2991 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2992 break;
2994 if (p && p[1]) RegCloseKey( hkey );
2995 else break;
2997 msi_free( subkey );
3000 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
3002 LONG res;
3003 HKEY hkey;
3004 DWORD num_subkeys, num_values;
3006 if ((hkey = open_key( root, path, FALSE )))
3008 if ((res = RegDeleteValueW( hkey, value )))
3009 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3011 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3012 NULL, NULL, NULL, NULL );
3013 RegCloseKey( hkey );
3014 if (!res && !num_subkeys && !num_values)
3016 TRACE("removing empty key %s\n", debugstr_w(path));
3017 delete_key( root, path );
3022 static void delete_tree( HKEY root, const WCHAR *path )
3024 LONG res;
3025 HKEY hkey;
3027 if (!(hkey = open_key( root, path, FALSE ))) return;
3028 res = RegDeleteTreeW( hkey, NULL );
3029 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3030 delete_key( root, path );
3031 RegCloseKey( hkey );
3034 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3036 MSIPACKAGE *package = param;
3037 LPCWSTR component, name, key_str, root_key_str;
3038 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3039 MSICOMPONENT *comp;
3040 MSIRECORD *uirow;
3041 BOOL delete_key = FALSE;
3042 HKEY hkey_root;
3043 UINT size;
3044 INT root;
3046 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3048 component = MSI_RecordGetString( row, 6 );
3049 comp = msi_get_loaded_component( package, component );
3050 if (!comp)
3051 return ERROR_SUCCESS;
3053 comp->Action = msi_get_component_action( package, comp );
3054 if (comp->Action != INSTALLSTATE_ABSENT)
3056 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3057 return ERROR_SUCCESS;
3060 name = MSI_RecordGetString( row, 4 );
3061 if (MSI_RecordIsNull( row, 5 ) && name )
3063 if (name[0] == '+' && !name[1])
3064 return ERROR_SUCCESS;
3065 if ((name[0] == '-' || name[0] == '*') && !name[1])
3067 delete_key = TRUE;
3068 name = NULL;
3072 root = MSI_RecordGetInteger( row, 2 );
3073 key_str = MSI_RecordGetString( row, 3 );
3075 root_key_str = get_root_key( package, root, &hkey_root );
3076 if (!root_key_str)
3077 return ERROR_SUCCESS;
3079 deformat_string( package, key_str, &deformated_key );
3080 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3081 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3082 strcpyW( ui_key_str, root_key_str );
3083 strcatW( ui_key_str, deformated_key );
3085 deformat_string( package, name, &deformated_name );
3087 keypath = get_keypath( comp, hkey_root, deformated_key );
3088 msi_free( deformated_key );
3089 if (delete_key) delete_tree( hkey_root, keypath );
3090 else delete_value( hkey_root, keypath, deformated_name );
3091 msi_free( keypath );
3093 uirow = MSI_CreateRecord( 2 );
3094 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3095 MSI_RecordSetStringW( uirow, 2, deformated_name );
3096 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3097 msiobj_release( &uirow->hdr );
3099 msi_free( ui_key_str );
3100 msi_free( deformated_name );
3101 return ERROR_SUCCESS;
3104 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3106 MSIPACKAGE *package = param;
3107 LPCWSTR component, name, key_str, root_key_str;
3108 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3109 MSICOMPONENT *comp;
3110 MSIRECORD *uirow;
3111 BOOL delete_key = FALSE;
3112 HKEY hkey_root;
3113 UINT size;
3114 INT root;
3116 component = MSI_RecordGetString( row, 5 );
3117 comp = msi_get_loaded_component( package, component );
3118 if (!comp)
3119 return ERROR_SUCCESS;
3121 comp->Action = msi_get_component_action( package, comp );
3122 if (comp->Action != INSTALLSTATE_LOCAL)
3124 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3125 return ERROR_SUCCESS;
3128 if ((name = MSI_RecordGetString( row, 4 )))
3130 if (name[0] == '-' && !name[1])
3132 delete_key = TRUE;
3133 name = NULL;
3137 root = MSI_RecordGetInteger( row, 2 );
3138 key_str = MSI_RecordGetString( row, 3 );
3140 root_key_str = get_root_key( package, root, &hkey_root );
3141 if (!root_key_str)
3142 return ERROR_SUCCESS;
3144 deformat_string( package, key_str, &deformated_key );
3145 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3146 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3147 strcpyW( ui_key_str, root_key_str );
3148 strcatW( ui_key_str, deformated_key );
3150 deformat_string( package, name, &deformated_name );
3152 keypath = get_keypath( comp, hkey_root, deformated_key );
3153 msi_free( deformated_key );
3154 if (delete_key) delete_tree( hkey_root, keypath );
3155 else delete_value( hkey_root, keypath, deformated_name );
3156 msi_free( keypath );
3158 uirow = MSI_CreateRecord( 2 );
3159 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3160 MSI_RecordSetStringW( uirow, 2, deformated_name );
3161 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3162 msiobj_release( &uirow->hdr );
3164 msi_free( ui_key_str );
3165 msi_free( deformated_name );
3166 return ERROR_SUCCESS;
3169 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3171 static const WCHAR registry_query[] = {
3172 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3173 '`','R','e','g','i','s','t','r','y','`',0};
3174 static const WCHAR remove_registry_query[] = {
3175 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3176 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3177 MSIQUERY *view;
3178 UINT rc;
3180 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3181 if (rc == ERROR_SUCCESS)
3183 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3184 msiobj_release( &view->hdr );
3185 if (rc != ERROR_SUCCESS)
3186 return rc;
3188 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3189 if (rc == ERROR_SUCCESS)
3191 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3192 msiobj_release( &view->hdr );
3193 if (rc != ERROR_SUCCESS)
3194 return rc;
3196 return ERROR_SUCCESS;
3199 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3201 package->script->CurrentlyScripting = TRUE;
3203 return ERROR_SUCCESS;
3207 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3209 static const WCHAR query[]= {
3210 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3211 '`','R','e','g','i','s','t','r','y','`',0};
3212 MSICOMPONENT *comp;
3213 DWORD total = 0, count = 0;
3214 MSIQUERY *view;
3215 MSIFEATURE *feature;
3216 MSIFILE *file;
3217 UINT rc;
3219 TRACE("InstallValidate\n");
3221 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3222 if (rc == ERROR_SUCCESS)
3224 rc = MSI_IterateRecords( view, &count, NULL, package );
3225 msiobj_release( &view->hdr );
3226 if (rc != ERROR_SUCCESS)
3227 return rc;
3228 total += count * REG_PROGRESS_VALUE;
3230 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3231 total += COMPONENT_PROGRESS_VALUE;
3233 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3234 total += file->FileSize;
3236 msi_ui_progress( package, 0, total, 0, 0 );
3238 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3240 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3241 debugstr_w(feature->Feature), feature->Installed,
3242 feature->ActionRequest, feature->Action);
3244 return ERROR_SUCCESS;
3247 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3249 MSIPACKAGE* package = param;
3250 LPCWSTR cond = NULL;
3251 LPCWSTR message = NULL;
3252 UINT r;
3254 static const WCHAR title[]=
3255 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3257 cond = MSI_RecordGetString(row,1);
3259 r = MSI_EvaluateConditionW(package,cond);
3260 if (r == MSICONDITION_FALSE)
3262 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3264 LPWSTR deformated;
3265 message = MSI_RecordGetString(row,2);
3266 deformat_string(package,message,&deformated);
3267 MessageBoxW(NULL,deformated,title,MB_OK);
3268 msi_free(deformated);
3271 return ERROR_INSTALL_FAILURE;
3274 return ERROR_SUCCESS;
3277 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3279 static const WCHAR query[] = {
3280 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3281 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3282 MSIQUERY *view;
3283 UINT rc;
3285 TRACE("Checking launch conditions\n");
3287 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3288 if (rc != ERROR_SUCCESS)
3289 return ERROR_SUCCESS;
3291 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3292 msiobj_release(&view->hdr);
3293 return rc;
3296 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3299 if (!cmp->KeyPath)
3300 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3302 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3304 static const WCHAR query[] = {
3305 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3306 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3307 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3308 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3309 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3310 MSIRECORD *row;
3311 UINT root, len;
3312 LPWSTR deformated, buffer, deformated_name;
3313 LPCWSTR key, name;
3315 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3316 if (!row)
3317 return NULL;
3319 root = MSI_RecordGetInteger(row,2);
3320 key = MSI_RecordGetString(row, 3);
3321 name = MSI_RecordGetString(row, 4);
3322 deformat_string(package, key , &deformated);
3323 deformat_string(package, name, &deformated_name);
3325 len = strlenW(deformated) + 6;
3326 if (deformated_name)
3327 len+=strlenW(deformated_name);
3329 buffer = msi_alloc( len *sizeof(WCHAR));
3331 if (deformated_name)
3332 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3333 else
3334 sprintfW(buffer,fmt,root,deformated);
3336 msi_free(deformated);
3337 msi_free(deformated_name);
3338 msiobj_release(&row->hdr);
3340 return buffer;
3342 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3344 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3345 return NULL;
3347 else
3349 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3351 if (file)
3352 return strdupW( file->TargetPath );
3354 return NULL;
3357 static HKEY openSharedDLLsKey(void)
3359 HKEY hkey=0;
3360 static const WCHAR path[] =
3361 {'S','o','f','t','w','a','r','e','\\',
3362 'M','i','c','r','o','s','o','f','t','\\',
3363 'W','i','n','d','o','w','s','\\',
3364 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3365 'S','h','a','r','e','d','D','L','L','s',0};
3367 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3368 return hkey;
3371 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3373 HKEY hkey;
3374 DWORD count=0;
3375 DWORD type;
3376 DWORD sz = sizeof(count);
3377 DWORD rc;
3379 hkey = openSharedDLLsKey();
3380 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3381 if (rc != ERROR_SUCCESS)
3382 count = 0;
3383 RegCloseKey(hkey);
3384 return count;
3387 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3389 HKEY hkey;
3391 hkey = openSharedDLLsKey();
3392 if (count > 0)
3393 msi_reg_set_val_dword( hkey, path, count );
3394 else
3395 RegDeleteValueW(hkey,path);
3396 RegCloseKey(hkey);
3397 return count;
3400 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3402 MSIFEATURE *feature;
3403 INT count = 0;
3404 BOOL write = FALSE;
3406 /* only refcount DLLs */
3407 if (comp->KeyPath == NULL ||
3408 comp->assembly ||
3409 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3410 comp->Attributes & msidbComponentAttributesODBCDataSource)
3411 write = FALSE;
3412 else
3414 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3415 write = (count > 0);
3417 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3418 write = TRUE;
3421 /* increment counts */
3422 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3424 ComponentList *cl;
3426 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3427 continue;
3429 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3431 if ( cl->component == comp )
3432 count++;
3436 /* decrement counts */
3437 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3439 ComponentList *cl;
3441 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3442 continue;
3444 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3446 if ( cl->component == comp )
3447 count--;
3451 /* ref count all the files in the component */
3452 if (write)
3454 MSIFILE *file;
3456 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3458 if (file->Component == comp)
3459 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3463 /* add a count for permanent */
3464 if (comp->Attributes & msidbComponentAttributesPermanent)
3465 count ++;
3467 comp->RefCount = count;
3469 if (write)
3470 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3473 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3475 if (comp->assembly)
3477 const WCHAR prefixW[] = {'<','\\',0};
3478 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3479 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3481 if (keypath)
3483 strcpyW( keypath, prefixW );
3484 strcatW( keypath, comp->assembly->display_name );
3486 return keypath;
3488 return resolve_keypath( package, comp );
3491 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3493 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3494 UINT rc;
3495 MSICOMPONENT *comp;
3496 HKEY hkey;
3498 TRACE("\n");
3500 squash_guid(package->ProductCode,squished_pc);
3501 msi_set_sourcedir_props(package, FALSE);
3503 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3505 MSIRECORD *uirow;
3506 INSTALLSTATE action;
3508 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3509 if (!comp->ComponentId)
3510 continue;
3512 squash_guid( comp->ComponentId, squished_cc );
3513 msi_free( comp->FullKeypath );
3514 comp->FullKeypath = build_full_keypath( package, comp );
3516 ACTION_RefCountComponent( package, comp );
3518 if (package->need_rollback) action = comp->Installed;
3519 else action = comp->ActionRequest;
3521 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3522 debugstr_w(comp->Component), debugstr_w(squished_cc),
3523 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3525 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3527 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3528 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3529 else
3530 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3532 if (rc != ERROR_SUCCESS)
3533 continue;
3535 if (comp->Attributes & msidbComponentAttributesPermanent)
3537 static const WCHAR szPermKey[] =
3538 { '0','0','0','0','0','0','0','0','0','0','0','0',
3539 '0','0','0','0','0','0','0','0','0','0','0','0',
3540 '0','0','0','0','0','0','0','0',0 };
3542 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3544 if (action == INSTALLSTATE_LOCAL)
3545 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3546 else
3548 MSIFILE *file;
3549 MSIRECORD *row;
3550 LPWSTR ptr, ptr2;
3551 WCHAR source[MAX_PATH];
3552 WCHAR base[MAX_PATH];
3553 LPWSTR sourcepath;
3555 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3556 static const WCHAR query[] = {
3557 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3558 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3559 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3560 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3561 '`','D','i','s','k','I','d','`',0};
3563 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3564 continue;
3566 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3567 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3568 ptr2 = strrchrW(source, '\\') + 1;
3569 msiobj_release(&row->hdr);
3571 lstrcpyW(base, package->PackagePath);
3572 ptr = strrchrW(base, '\\');
3573 *(ptr + 1) = '\0';
3575 sourcepath = msi_resolve_file_source(package, file);
3576 ptr = sourcepath + lstrlenW(base);
3577 lstrcpyW(ptr2, ptr);
3578 msi_free(sourcepath);
3580 msi_reg_set_val_str(hkey, squished_pc, source);
3582 RegCloseKey(hkey);
3584 else if (action == INSTALLSTATE_ABSENT)
3586 if (comp->num_clients <= 0)
3588 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3589 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3590 else
3591 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3595 /* UI stuff */
3596 uirow = MSI_CreateRecord(3);
3597 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3598 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3599 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3600 msi_ui_actiondata( package, szProcessComponents, uirow );
3601 msiobj_release( &uirow->hdr );
3603 return ERROR_SUCCESS;
3606 typedef struct {
3607 CLSID clsid;
3608 LPWSTR source;
3610 LPWSTR path;
3611 ITypeLib *ptLib;
3612 } typelib_struct;
3614 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3615 LPWSTR lpszName, LONG_PTR lParam)
3617 TLIBATTR *attr;
3618 typelib_struct *tl_struct = (typelib_struct*) lParam;
3619 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3620 int sz;
3621 HRESULT res;
3623 if (!IS_INTRESOURCE(lpszName))
3625 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3626 return TRUE;
3629 sz = strlenW(tl_struct->source)+4;
3630 sz *= sizeof(WCHAR);
3632 if ((INT_PTR)lpszName == 1)
3633 tl_struct->path = strdupW(tl_struct->source);
3634 else
3636 tl_struct->path = msi_alloc(sz);
3637 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3640 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3641 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3642 if (FAILED(res))
3644 msi_free(tl_struct->path);
3645 tl_struct->path = NULL;
3647 return TRUE;
3650 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3651 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3653 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3654 return FALSE;
3657 msi_free(tl_struct->path);
3658 tl_struct->path = NULL;
3660 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3661 ITypeLib_Release(tl_struct->ptLib);
3663 return TRUE;
3666 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3668 MSIPACKAGE* package = param;
3669 LPCWSTR component;
3670 MSICOMPONENT *comp;
3671 MSIFILE *file;
3672 typelib_struct tl_struct;
3673 ITypeLib *tlib;
3674 HMODULE module;
3675 HRESULT hr;
3677 component = MSI_RecordGetString(row,3);
3678 comp = msi_get_loaded_component(package,component);
3679 if (!comp)
3680 return ERROR_SUCCESS;
3682 comp->Action = msi_get_component_action( package, comp );
3683 if (comp->Action != INSTALLSTATE_LOCAL)
3685 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3686 return ERROR_SUCCESS;
3689 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3691 TRACE("component has no key path\n");
3692 return ERROR_SUCCESS;
3694 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3696 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3697 if (module)
3699 LPCWSTR guid;
3700 guid = MSI_RecordGetString(row,1);
3701 CLSIDFromString( guid, &tl_struct.clsid);
3702 tl_struct.source = strdupW( file->TargetPath );
3703 tl_struct.path = NULL;
3705 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3706 (LONG_PTR)&tl_struct);
3708 if (tl_struct.path)
3710 LPCWSTR helpid, help_path = NULL;
3711 HRESULT res;
3713 helpid = MSI_RecordGetString(row,6);
3715 if (helpid) help_path = msi_get_target_folder( package, helpid );
3716 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3718 if (FAILED(res))
3719 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3720 else
3721 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3723 ITypeLib_Release(tl_struct.ptLib);
3724 msi_free(tl_struct.path);
3726 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3728 FreeLibrary(module);
3729 msi_free(tl_struct.source);
3731 else
3733 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3734 if (FAILED(hr))
3736 ERR("Failed to load type library: %08x\n", hr);
3737 return ERROR_INSTALL_FAILURE;
3740 ITypeLib_Release(tlib);
3743 return ERROR_SUCCESS;
3746 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3748 static const WCHAR query[] = {
3749 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3750 '`','T','y','p','e','L','i','b','`',0};
3751 MSIQUERY *view;
3752 UINT rc;
3754 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3755 if (rc != ERROR_SUCCESS)
3756 return ERROR_SUCCESS;
3758 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3759 msiobj_release(&view->hdr);
3760 return rc;
3763 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3765 MSIPACKAGE *package = param;
3766 LPCWSTR component, guid;
3767 MSICOMPONENT *comp;
3768 GUID libid;
3769 UINT version;
3770 LCID language;
3771 SYSKIND syskind;
3772 HRESULT hr;
3774 component = MSI_RecordGetString( row, 3 );
3775 comp = msi_get_loaded_component( package, component );
3776 if (!comp)
3777 return ERROR_SUCCESS;
3779 comp->Action = msi_get_component_action( package, comp );
3780 if (comp->Action != INSTALLSTATE_ABSENT)
3782 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3783 return ERROR_SUCCESS;
3785 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3787 guid = MSI_RecordGetString( row, 1 );
3788 CLSIDFromString( guid, &libid );
3789 version = MSI_RecordGetInteger( row, 4 );
3790 language = MSI_RecordGetInteger( row, 2 );
3792 #ifdef _WIN64
3793 syskind = SYS_WIN64;
3794 #else
3795 syskind = SYS_WIN32;
3796 #endif
3798 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3799 if (FAILED(hr))
3801 WARN("Failed to unregister typelib: %08x\n", hr);
3804 return ERROR_SUCCESS;
3807 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3809 static const WCHAR query[] = {
3810 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3811 '`','T','y','p','e','L','i','b','`',0};
3812 MSIQUERY *view;
3813 UINT rc;
3815 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3816 if (rc != ERROR_SUCCESS)
3817 return ERROR_SUCCESS;
3819 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3820 msiobj_release( &view->hdr );
3821 return rc;
3824 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3826 static const WCHAR szlnk[] = {'.','l','n','k',0};
3827 LPCWSTR directory, extension, link_folder;
3828 LPWSTR link_file, filename;
3830 directory = MSI_RecordGetString( row, 2 );
3831 link_folder = msi_get_target_folder( package, directory );
3832 if (!link_folder)
3834 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3835 return NULL;
3837 /* may be needed because of a bug somewhere else */
3838 msi_create_full_path( link_folder );
3840 filename = msi_dup_record_field( row, 3 );
3841 msi_reduce_to_long_filename( filename );
3843 extension = strrchrW( filename, '.' );
3844 if (!extension || strcmpiW( extension, szlnk ))
3846 int len = strlenW( filename );
3847 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3848 memcpy( filename + len, szlnk, sizeof(szlnk) );
3850 link_file = msi_build_directory_name( 2, link_folder, filename );
3851 msi_free( filename );
3853 return link_file;
3856 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3858 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3859 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3860 WCHAR *folder, *dest, *path;
3862 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3863 folder = msi_dup_property( package->db, szWindowsFolder );
3864 else
3866 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3867 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3868 msi_free( appdata );
3870 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3871 msi_create_full_path( dest );
3872 path = msi_build_directory_name( 2, dest, icon_name );
3873 msi_free( folder );
3874 msi_free( dest );
3875 return path;
3878 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3880 MSIPACKAGE *package = param;
3881 LPWSTR link_file, deformated, path;
3882 LPCWSTR component, target;
3883 MSICOMPONENT *comp;
3884 IShellLinkW *sl = NULL;
3885 IPersistFile *pf = NULL;
3886 HRESULT res;
3888 component = MSI_RecordGetString(row, 4);
3889 comp = msi_get_loaded_component(package, component);
3890 if (!comp)
3891 return ERROR_SUCCESS;
3893 comp->Action = msi_get_component_action( package, comp );
3894 if (comp->Action != INSTALLSTATE_LOCAL)
3896 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3897 return ERROR_SUCCESS;
3899 msi_ui_actiondata( package, szCreateShortcuts, row );
3901 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3902 &IID_IShellLinkW, (LPVOID *) &sl );
3904 if (FAILED( res ))
3906 ERR("CLSID_ShellLink not available\n");
3907 goto err;
3910 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3911 if (FAILED( res ))
3913 ERR("QueryInterface(IID_IPersistFile) failed\n");
3914 goto err;
3917 target = MSI_RecordGetString(row, 5);
3918 if (strchrW(target, '['))
3920 deformat_string( package, target, &path );
3921 TRACE("target path is %s\n", debugstr_w(path));
3922 IShellLinkW_SetPath( sl, path );
3923 msi_free( path );
3925 else
3927 FIXME("poorly handled shortcut format, advertised shortcut\n");
3928 IShellLinkW_SetPath(sl,comp->FullKeypath);
3931 if (!MSI_RecordIsNull(row,6))
3933 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3934 deformat_string(package, arguments, &deformated);
3935 IShellLinkW_SetArguments(sl,deformated);
3936 msi_free(deformated);
3939 if (!MSI_RecordIsNull(row,7))
3941 LPCWSTR description = MSI_RecordGetString(row, 7);
3942 IShellLinkW_SetDescription(sl, description);
3945 if (!MSI_RecordIsNull(row,8))
3946 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3948 if (!MSI_RecordIsNull(row,9))
3950 INT index;
3951 LPCWSTR icon = MSI_RecordGetString(row, 9);
3953 path = msi_build_icon_path(package, icon);
3954 index = MSI_RecordGetInteger(row,10);
3956 /* no value means 0 */
3957 if (index == MSI_NULL_INTEGER)
3958 index = 0;
3960 IShellLinkW_SetIconLocation(sl, path, index);
3961 msi_free(path);
3964 if (!MSI_RecordIsNull(row,11))
3965 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3967 if (!MSI_RecordIsNull(row,12))
3969 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3970 full_path = msi_get_target_folder( package, wkdir );
3971 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3973 link_file = get_link_file(package, row);
3975 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3976 IPersistFile_Save(pf, link_file, FALSE);
3977 msi_free(link_file);
3979 err:
3980 if (pf)
3981 IPersistFile_Release( pf );
3982 if (sl)
3983 IShellLinkW_Release( sl );
3985 return ERROR_SUCCESS;
3988 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3990 static const WCHAR query[] = {
3991 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3992 '`','S','h','o','r','t','c','u','t','`',0};
3993 MSIQUERY *view;
3994 HRESULT res;
3995 UINT rc;
3997 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3998 if (rc != ERROR_SUCCESS)
3999 return ERROR_SUCCESS;
4001 res = CoInitialize( NULL );
4003 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4004 msiobj_release(&view->hdr);
4006 if (SUCCEEDED(res)) CoUninitialize();
4007 return rc;
4010 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4012 MSIPACKAGE *package = param;
4013 LPWSTR link_file;
4014 LPCWSTR component;
4015 MSICOMPONENT *comp;
4017 component = MSI_RecordGetString( row, 4 );
4018 comp = msi_get_loaded_component( package, component );
4019 if (!comp)
4020 return ERROR_SUCCESS;
4022 comp->Action = msi_get_component_action( package, comp );
4023 if (comp->Action != INSTALLSTATE_ABSENT)
4025 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4026 return ERROR_SUCCESS;
4028 msi_ui_actiondata( package, szRemoveShortcuts, row );
4030 link_file = get_link_file( package, row );
4032 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4033 if (!DeleteFileW( link_file ))
4035 WARN("Failed to remove shortcut file %u\n", GetLastError());
4037 msi_free( link_file );
4039 return ERROR_SUCCESS;
4042 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4044 static const WCHAR query[] = {
4045 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4046 '`','S','h','o','r','t','c','u','t','`',0};
4047 MSIQUERY *view;
4048 UINT rc;
4050 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4051 if (rc != ERROR_SUCCESS)
4052 return ERROR_SUCCESS;
4054 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4055 msiobj_release( &view->hdr );
4056 return rc;
4059 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4061 MSIPACKAGE* package = param;
4062 HANDLE the_file;
4063 LPWSTR FilePath;
4064 LPCWSTR FileName;
4065 CHAR buffer[1024];
4066 DWORD sz;
4067 UINT rc;
4069 FileName = MSI_RecordGetString(row,1);
4070 if (!FileName)
4072 ERR("Unable to get FileName\n");
4073 return ERROR_SUCCESS;
4076 FilePath = msi_build_icon_path(package, FileName);
4078 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4080 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4081 FILE_ATTRIBUTE_NORMAL, NULL);
4083 if (the_file == INVALID_HANDLE_VALUE)
4085 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4086 msi_free(FilePath);
4087 return ERROR_SUCCESS;
4092 DWORD write;
4093 sz = 1024;
4094 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4095 if (rc != ERROR_SUCCESS)
4097 ERR("Failed to get stream\n");
4098 CloseHandle(the_file);
4099 DeleteFileW(FilePath);
4100 break;
4102 WriteFile(the_file,buffer,sz,&write,NULL);
4103 } while (sz == 1024);
4105 msi_free(FilePath);
4106 CloseHandle(the_file);
4108 return ERROR_SUCCESS;
4111 static UINT msi_publish_icons(MSIPACKAGE *package)
4113 static const WCHAR query[]= {
4114 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4115 '`','I','c','o','n','`',0};
4116 MSIQUERY *view;
4117 UINT r;
4119 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4120 if (r == ERROR_SUCCESS)
4122 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4123 msiobj_release(&view->hdr);
4124 if (r != ERROR_SUCCESS)
4125 return r;
4127 return ERROR_SUCCESS;
4130 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4132 UINT r;
4133 HKEY source;
4134 LPWSTR buffer;
4135 MSIMEDIADISK *disk;
4136 MSISOURCELISTINFO *info;
4138 r = RegCreateKeyW(hkey, szSourceList, &source);
4139 if (r != ERROR_SUCCESS)
4140 return r;
4142 RegCloseKey(source);
4144 buffer = strrchrW(package->PackagePath, '\\') + 1;
4145 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4146 package->Context, MSICODE_PRODUCT,
4147 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4148 if (r != ERROR_SUCCESS)
4149 return r;
4151 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4152 package->Context, MSICODE_PRODUCT,
4153 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4154 if (r != ERROR_SUCCESS)
4155 return r;
4157 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4158 package->Context, MSICODE_PRODUCT,
4159 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4160 if (r != ERROR_SUCCESS)
4161 return r;
4163 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4165 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4166 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4167 info->options, info->value);
4168 else
4169 MsiSourceListSetInfoW(package->ProductCode, NULL,
4170 info->context, info->options,
4171 info->property, info->value);
4174 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4176 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4177 disk->context, disk->options,
4178 disk->disk_id, disk->volume_label, disk->disk_prompt);
4181 return ERROR_SUCCESS;
4184 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4186 MSIHANDLE hdb, suminfo;
4187 WCHAR guids[MAX_PATH];
4188 WCHAR packcode[SQUISH_GUID_SIZE];
4189 LPWSTR buffer;
4190 LPWSTR ptr;
4191 DWORD langid;
4192 DWORD size;
4193 UINT r;
4195 static const WCHAR szARPProductIcon[] =
4196 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4197 static const WCHAR szAssignment[] =
4198 {'A','s','s','i','g','n','m','e','n','t',0};
4199 static const WCHAR szAdvertiseFlags[] =
4200 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4201 static const WCHAR szClients[] =
4202 {'C','l','i','e','n','t','s',0};
4203 static const WCHAR szColon[] = {':',0};
4205 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4206 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4207 msi_free(buffer);
4209 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4210 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4212 /* FIXME */
4213 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4215 buffer = msi_dup_property(package->db, szARPProductIcon);
4216 if (buffer)
4218 LPWSTR path = msi_build_icon_path(package, buffer);
4219 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4220 msi_free(path);
4221 msi_free(buffer);
4224 buffer = msi_dup_property(package->db, szProductVersion);
4225 if (buffer)
4227 DWORD verdword = msi_version_str_to_dword(buffer);
4228 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4229 msi_free(buffer);
4232 msi_reg_set_val_dword(hkey, szAssignment, 0);
4233 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4234 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4235 msi_reg_set_val_str(hkey, szClients, szColon);
4237 hdb = alloc_msihandle(&package->db->hdr);
4238 if (!hdb)
4239 return ERROR_NOT_ENOUGH_MEMORY;
4241 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4242 MsiCloseHandle(hdb);
4243 if (r != ERROR_SUCCESS)
4244 goto done;
4246 size = MAX_PATH;
4247 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4248 NULL, guids, &size);
4249 if (r != ERROR_SUCCESS)
4250 goto done;
4252 ptr = strchrW(guids, ';');
4253 if (ptr) *ptr = 0;
4254 squash_guid(guids, packcode);
4255 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4257 done:
4258 MsiCloseHandle(suminfo);
4259 return ERROR_SUCCESS;
4262 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4264 UINT r;
4265 HKEY hkey;
4266 LPWSTR upgrade;
4267 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4269 upgrade = msi_dup_property(package->db, szUpgradeCode);
4270 if (!upgrade)
4271 return ERROR_SUCCESS;
4273 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4274 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4275 else
4276 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4278 if (r != ERROR_SUCCESS)
4280 WARN("failed to open upgrade code key\n");
4281 msi_free(upgrade);
4282 return ERROR_SUCCESS;
4284 squash_guid(package->ProductCode, squashed_pc);
4285 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4286 RegCloseKey(hkey);
4287 msi_free(upgrade);
4288 return ERROR_SUCCESS;
4291 static BOOL msi_check_publish(MSIPACKAGE *package)
4293 MSIFEATURE *feature;
4295 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4297 feature->Action = msi_get_feature_action( package, feature );
4298 if (feature->Action == INSTALLSTATE_LOCAL)
4299 return TRUE;
4302 return FALSE;
4305 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4307 MSIFEATURE *feature;
4309 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4311 feature->Action = msi_get_feature_action( package, feature );
4312 if (feature->Action != INSTALLSTATE_ABSENT)
4313 return FALSE;
4316 return TRUE;
4319 static UINT msi_publish_patches( MSIPACKAGE *package )
4321 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4322 WCHAR patch_squashed[GUID_SIZE];
4323 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4324 LONG res;
4325 MSIPATCHINFO *patch;
4326 UINT r;
4327 WCHAR *p, *all_patches = NULL;
4328 DWORD len = 0;
4330 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4331 if (r != ERROR_SUCCESS)
4332 return ERROR_FUNCTION_FAILED;
4334 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4335 if (res != ERROR_SUCCESS)
4337 r = ERROR_FUNCTION_FAILED;
4338 goto done;
4341 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4342 if (r != ERROR_SUCCESS)
4343 goto done;
4345 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4347 squash_guid( patch->patchcode, patch_squashed );
4348 len += strlenW( patch_squashed ) + 1;
4351 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4352 if (!all_patches)
4353 goto done;
4355 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4357 HKEY patch_key;
4359 squash_guid( patch->patchcode, p );
4360 p += strlenW( p ) + 1;
4362 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4363 (const BYTE *)patch->transforms,
4364 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4365 if (res != ERROR_SUCCESS)
4366 goto done;
4368 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4369 if (r != ERROR_SUCCESS)
4370 goto done;
4372 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4373 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4374 RegCloseKey( patch_key );
4375 if (res != ERROR_SUCCESS)
4376 goto done;
4378 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4380 res = GetLastError();
4381 ERR("Unable to copy patch package %d\n", res);
4382 goto done;
4384 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4385 if (res != ERROR_SUCCESS)
4386 goto done;
4388 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4389 RegCloseKey( patch_key );
4390 if (res != ERROR_SUCCESS)
4391 goto done;
4394 all_patches[len] = 0;
4395 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4396 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4397 if (res != ERROR_SUCCESS)
4398 goto done;
4400 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4401 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4402 if (res != ERROR_SUCCESS)
4403 r = ERROR_FUNCTION_FAILED;
4405 done:
4406 RegCloseKey( product_patches_key );
4407 RegCloseKey( patches_key );
4408 RegCloseKey( product_key );
4409 msi_free( all_patches );
4410 return r;
4413 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4415 UINT rc;
4416 HKEY hukey = NULL, hudkey = NULL;
4417 MSIRECORD *uirow;
4419 if (!list_empty(&package->patches))
4421 rc = msi_publish_patches(package);
4422 if (rc != ERROR_SUCCESS)
4423 goto end;
4426 /* FIXME: also need to publish if the product is in advertise mode */
4427 if (!msi_check_publish(package))
4428 return ERROR_SUCCESS;
4430 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4431 &hukey, TRUE);
4432 if (rc != ERROR_SUCCESS)
4433 goto end;
4435 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4436 NULL, &hudkey, TRUE);
4437 if (rc != ERROR_SUCCESS)
4438 goto end;
4440 rc = msi_publish_upgrade_code(package);
4441 if (rc != ERROR_SUCCESS)
4442 goto end;
4444 rc = msi_publish_product_properties(package, hukey);
4445 if (rc != ERROR_SUCCESS)
4446 goto end;
4448 rc = msi_publish_sourcelist(package, hukey);
4449 if (rc != ERROR_SUCCESS)
4450 goto end;
4452 rc = msi_publish_icons(package);
4454 end:
4455 uirow = MSI_CreateRecord( 1 );
4456 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4457 msi_ui_actiondata( package, szPublishProduct, uirow );
4458 msiobj_release( &uirow->hdr );
4460 RegCloseKey(hukey);
4461 RegCloseKey(hudkey);
4462 return rc;
4465 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4467 WCHAR *filename, *ptr, *folder, *ret;
4468 const WCHAR *dirprop;
4470 filename = msi_dup_record_field( row, 2 );
4471 if (filename && (ptr = strchrW( filename, '|' )))
4472 ptr++;
4473 else
4474 ptr = filename;
4476 dirprop = MSI_RecordGetString( row, 3 );
4477 if (dirprop)
4479 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4480 if (!folder) folder = msi_dup_property( package->db, dirprop );
4482 else
4483 folder = msi_dup_property( package->db, szWindowsFolder );
4485 if (!folder)
4487 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4488 msi_free( filename );
4489 return NULL;
4492 ret = msi_build_directory_name( 2, folder, ptr );
4494 msi_free( filename );
4495 msi_free( folder );
4496 return ret;
4499 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4501 MSIPACKAGE *package = param;
4502 LPCWSTR component, section, key, value, identifier;
4503 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4504 MSIRECORD * uirow;
4505 INT action;
4506 MSICOMPONENT *comp;
4508 component = MSI_RecordGetString(row, 8);
4509 comp = msi_get_loaded_component(package,component);
4510 if (!comp)
4511 return ERROR_SUCCESS;
4513 comp->Action = msi_get_component_action( package, comp );
4514 if (comp->Action != INSTALLSTATE_LOCAL)
4516 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4517 return ERROR_SUCCESS;
4520 identifier = MSI_RecordGetString(row,1);
4521 section = MSI_RecordGetString(row,4);
4522 key = MSI_RecordGetString(row,5);
4523 value = MSI_RecordGetString(row,6);
4524 action = MSI_RecordGetInteger(row,7);
4526 deformat_string(package,section,&deformated_section);
4527 deformat_string(package,key,&deformated_key);
4528 deformat_string(package,value,&deformated_value);
4530 fullname = get_ini_file_name(package, row);
4532 if (action == 0)
4534 TRACE("Adding value %s to section %s in %s\n",
4535 debugstr_w(deformated_key), debugstr_w(deformated_section),
4536 debugstr_w(fullname));
4537 WritePrivateProfileStringW(deformated_section, deformated_key,
4538 deformated_value, fullname);
4540 else if (action == 1)
4542 WCHAR returned[10];
4543 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4544 returned, 10, fullname);
4545 if (returned[0] == 0)
4547 TRACE("Adding value %s to section %s in %s\n",
4548 debugstr_w(deformated_key), debugstr_w(deformated_section),
4549 debugstr_w(fullname));
4551 WritePrivateProfileStringW(deformated_section, deformated_key,
4552 deformated_value, fullname);
4555 else if (action == 3)
4556 FIXME("Append to existing section not yet implemented\n");
4558 uirow = MSI_CreateRecord(4);
4559 MSI_RecordSetStringW(uirow,1,identifier);
4560 MSI_RecordSetStringW(uirow,2,deformated_section);
4561 MSI_RecordSetStringW(uirow,3,deformated_key);
4562 MSI_RecordSetStringW(uirow,4,deformated_value);
4563 msi_ui_actiondata( package, szWriteIniValues, uirow );
4564 msiobj_release( &uirow->hdr );
4566 msi_free(fullname);
4567 msi_free(deformated_key);
4568 msi_free(deformated_value);
4569 msi_free(deformated_section);
4570 return ERROR_SUCCESS;
4573 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4575 static const WCHAR query[] = {
4576 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4577 '`','I','n','i','F','i','l','e','`',0};
4578 MSIQUERY *view;
4579 UINT rc;
4581 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4582 if (rc != ERROR_SUCCESS)
4583 return ERROR_SUCCESS;
4585 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4586 msiobj_release(&view->hdr);
4587 return rc;
4590 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4592 MSIPACKAGE *package = param;
4593 LPCWSTR component, section, key, value, identifier;
4594 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4595 MSICOMPONENT *comp;
4596 MSIRECORD *uirow;
4597 INT action;
4599 component = MSI_RecordGetString( row, 8 );
4600 comp = msi_get_loaded_component( package, component );
4601 if (!comp)
4602 return ERROR_SUCCESS;
4604 comp->Action = msi_get_component_action( package, comp );
4605 if (comp->Action != INSTALLSTATE_ABSENT)
4607 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4608 return ERROR_SUCCESS;
4611 identifier = MSI_RecordGetString( row, 1 );
4612 section = MSI_RecordGetString( row, 4 );
4613 key = MSI_RecordGetString( row, 5 );
4614 value = MSI_RecordGetString( row, 6 );
4615 action = MSI_RecordGetInteger( row, 7 );
4617 deformat_string( package, section, &deformated_section );
4618 deformat_string( package, key, &deformated_key );
4619 deformat_string( package, value, &deformated_value );
4621 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4623 filename = get_ini_file_name( package, row );
4625 TRACE("Removing key %s from section %s in %s\n",
4626 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4628 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4630 WARN("Unable to remove key %u\n", GetLastError());
4632 msi_free( filename );
4634 else
4635 FIXME("Unsupported action %d\n", action);
4638 uirow = MSI_CreateRecord( 4 );
4639 MSI_RecordSetStringW( uirow, 1, identifier );
4640 MSI_RecordSetStringW( uirow, 2, deformated_section );
4641 MSI_RecordSetStringW( uirow, 3, deformated_key );
4642 MSI_RecordSetStringW( uirow, 4, deformated_value );
4643 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4644 msiobj_release( &uirow->hdr );
4646 msi_free( deformated_key );
4647 msi_free( deformated_value );
4648 msi_free( deformated_section );
4649 return ERROR_SUCCESS;
4652 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4654 MSIPACKAGE *package = param;
4655 LPCWSTR component, section, key, value, identifier;
4656 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4657 MSICOMPONENT *comp;
4658 MSIRECORD *uirow;
4659 INT action;
4661 component = MSI_RecordGetString( row, 8 );
4662 comp = msi_get_loaded_component( package, component );
4663 if (!comp)
4664 return ERROR_SUCCESS;
4666 comp->Action = msi_get_component_action( package, comp );
4667 if (comp->Action != INSTALLSTATE_LOCAL)
4669 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4670 return ERROR_SUCCESS;
4673 identifier = MSI_RecordGetString( row, 1 );
4674 section = MSI_RecordGetString( row, 4 );
4675 key = MSI_RecordGetString( row, 5 );
4676 value = MSI_RecordGetString( row, 6 );
4677 action = MSI_RecordGetInteger( row, 7 );
4679 deformat_string( package, section, &deformated_section );
4680 deformat_string( package, key, &deformated_key );
4681 deformat_string( package, value, &deformated_value );
4683 if (action == msidbIniFileActionRemoveLine)
4685 filename = get_ini_file_name( package, row );
4687 TRACE("Removing key %s from section %s in %s\n",
4688 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4690 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4692 WARN("Unable to remove key %u\n", GetLastError());
4694 msi_free( filename );
4696 else
4697 FIXME("Unsupported action %d\n", action);
4699 uirow = MSI_CreateRecord( 4 );
4700 MSI_RecordSetStringW( uirow, 1, identifier );
4701 MSI_RecordSetStringW( uirow, 2, deformated_section );
4702 MSI_RecordSetStringW( uirow, 3, deformated_key );
4703 MSI_RecordSetStringW( uirow, 4, deformated_value );
4704 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4705 msiobj_release( &uirow->hdr );
4707 msi_free( deformated_key );
4708 msi_free( deformated_value );
4709 msi_free( deformated_section );
4710 return ERROR_SUCCESS;
4713 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4715 static const WCHAR query[] = {
4716 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4717 '`','I','n','i','F','i','l','e','`',0};
4718 static const WCHAR remove_query[] = {
4719 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4720 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4721 MSIQUERY *view;
4722 UINT rc;
4724 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4725 if (rc == ERROR_SUCCESS)
4727 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4728 msiobj_release( &view->hdr );
4729 if (rc != ERROR_SUCCESS)
4730 return rc;
4732 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4733 if (rc == ERROR_SUCCESS)
4735 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4736 msiobj_release( &view->hdr );
4737 if (rc != ERROR_SUCCESS)
4738 return rc;
4740 return ERROR_SUCCESS;
4743 static void register_dll( const WCHAR *dll, BOOL unregister )
4745 static const WCHAR regW[] =
4746 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4747 static const WCHAR unregW[] =
4748 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4749 PROCESS_INFORMATION pi;
4750 STARTUPINFOW si;
4751 WCHAR *cmd;
4753 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4755 if (unregister) sprintfW( cmd, unregW, dll );
4756 else sprintfW( cmd, regW, dll );
4758 memset( &si, 0, sizeof(STARTUPINFOW) );
4759 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4761 CloseHandle( pi.hThread );
4762 msi_dialog_check_messages( pi.hProcess );
4763 CloseHandle( pi.hProcess );
4765 msi_free( cmd );
4768 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4770 MSIPACKAGE *package = param;
4771 LPCWSTR filename;
4772 MSIFILE *file;
4773 MSIRECORD *uirow;
4775 filename = MSI_RecordGetString( row, 1 );
4776 file = msi_get_loaded_file( package, filename );
4777 if (!file)
4779 WARN("unable to find file %s\n", debugstr_w(filename));
4780 return ERROR_SUCCESS;
4782 file->Component->Action = msi_get_component_action( package, file->Component );
4783 if (file->Component->Action != INSTALLSTATE_LOCAL)
4785 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4786 return ERROR_SUCCESS;
4789 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4790 register_dll( file->TargetPath, FALSE );
4792 uirow = MSI_CreateRecord( 2 );
4793 MSI_RecordSetStringW( uirow, 1, file->File );
4794 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4795 msi_ui_actiondata( package, szSelfRegModules, uirow );
4796 msiobj_release( &uirow->hdr );
4798 return ERROR_SUCCESS;
4801 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4803 static const WCHAR query[] = {
4804 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4805 '`','S','e','l','f','R','e','g','`',0};
4806 MSIQUERY *view;
4807 UINT rc;
4809 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4810 if (rc != ERROR_SUCCESS)
4811 return ERROR_SUCCESS;
4813 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4814 msiobj_release(&view->hdr);
4815 return rc;
4818 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4820 MSIPACKAGE *package = param;
4821 LPCWSTR filename;
4822 MSIFILE *file;
4823 MSIRECORD *uirow;
4825 filename = MSI_RecordGetString( row, 1 );
4826 file = msi_get_loaded_file( package, filename );
4827 if (!file)
4829 WARN("unable to find file %s\n", debugstr_w(filename));
4830 return ERROR_SUCCESS;
4832 file->Component->Action = msi_get_component_action( package, file->Component );
4833 if (file->Component->Action != INSTALLSTATE_ABSENT)
4835 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4836 return ERROR_SUCCESS;
4839 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4840 register_dll( file->TargetPath, TRUE );
4842 uirow = MSI_CreateRecord( 2 );
4843 MSI_RecordSetStringW( uirow, 1, file->File );
4844 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4845 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4846 msiobj_release( &uirow->hdr );
4848 return ERROR_SUCCESS;
4851 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4853 static const WCHAR query[] = {
4854 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4855 '`','S','e','l','f','R','e','g','`',0};
4856 MSIQUERY *view;
4857 UINT rc;
4859 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4860 if (rc != ERROR_SUCCESS)
4861 return ERROR_SUCCESS;
4863 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4864 msiobj_release( &view->hdr );
4865 return rc;
4868 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4870 MSIFEATURE *feature;
4871 UINT rc;
4872 HKEY hkey = NULL, userdata = NULL;
4874 if (!msi_check_publish(package))
4875 return ERROR_SUCCESS;
4877 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4878 &hkey, TRUE);
4879 if (rc != ERROR_SUCCESS)
4880 goto end;
4882 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4883 &userdata, TRUE);
4884 if (rc != ERROR_SUCCESS)
4885 goto end;
4887 /* here the guids are base 85 encoded */
4888 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4890 ComponentList *cl;
4891 LPWSTR data = NULL;
4892 GUID clsid;
4893 INT size;
4894 BOOL absent = FALSE;
4895 MSIRECORD *uirow;
4897 if (feature->Action != INSTALLSTATE_LOCAL &&
4898 feature->Action != INSTALLSTATE_SOURCE &&
4899 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4901 size = 1;
4902 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4904 size += 21;
4906 if (feature->Feature_Parent)
4907 size += strlenW( feature->Feature_Parent )+2;
4909 data = msi_alloc(size * sizeof(WCHAR));
4911 data[0] = 0;
4912 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4914 MSICOMPONENT* component = cl->component;
4915 WCHAR buf[21];
4917 buf[0] = 0;
4918 if (component->ComponentId)
4920 TRACE("From %s\n",debugstr_w(component->ComponentId));
4921 CLSIDFromString(component->ComponentId, &clsid);
4922 encode_base85_guid(&clsid,buf);
4923 TRACE("to %s\n",debugstr_w(buf));
4924 strcatW(data,buf);
4928 if (feature->Feature_Parent)
4930 static const WCHAR sep[] = {'\2',0};
4931 strcatW(data,sep);
4932 strcatW(data,feature->Feature_Parent);
4935 msi_reg_set_val_str( userdata, feature->Feature, data );
4936 msi_free(data);
4938 size = 0;
4939 if (feature->Feature_Parent)
4940 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4941 if (!absent)
4943 size += sizeof(WCHAR);
4944 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4945 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4947 else
4949 size += 2*sizeof(WCHAR);
4950 data = msi_alloc(size);
4951 data[0] = 0x6;
4952 data[1] = 0;
4953 if (feature->Feature_Parent)
4954 strcpyW( &data[1], feature->Feature_Parent );
4955 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4956 (LPBYTE)data,size);
4957 msi_free(data);
4960 /* the UI chunk */
4961 uirow = MSI_CreateRecord( 1 );
4962 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4963 msi_ui_actiondata( package, szPublishFeatures, uirow );
4964 msiobj_release( &uirow->hdr );
4965 /* FIXME: call msi_ui_progress? */
4968 end:
4969 RegCloseKey(hkey);
4970 RegCloseKey(userdata);
4971 return rc;
4974 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4976 UINT r;
4977 HKEY hkey;
4978 MSIRECORD *uirow;
4980 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4982 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4983 &hkey, FALSE);
4984 if (r == ERROR_SUCCESS)
4986 RegDeleteValueW(hkey, feature->Feature);
4987 RegCloseKey(hkey);
4990 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4991 &hkey, FALSE);
4992 if (r == ERROR_SUCCESS)
4994 RegDeleteValueW(hkey, feature->Feature);
4995 RegCloseKey(hkey);
4998 uirow = MSI_CreateRecord( 1 );
4999 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5000 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5001 msiobj_release( &uirow->hdr );
5003 return ERROR_SUCCESS;
5006 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5008 MSIFEATURE *feature;
5010 if (!msi_check_unpublish(package))
5011 return ERROR_SUCCESS;
5013 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5015 msi_unpublish_feature(package, feature);
5018 return ERROR_SUCCESS;
5021 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5023 SYSTEMTIME systime;
5024 DWORD size, langid;
5025 WCHAR date[9], *val, *buffer;
5026 const WCHAR *prop, *key;
5028 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5029 static const WCHAR modpath_fmt[] =
5030 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5031 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5032 static const WCHAR szModifyPath[] =
5033 {'M','o','d','i','f','y','P','a','t','h',0};
5034 static const WCHAR szUninstallString[] =
5035 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5036 static const WCHAR szEstimatedSize[] =
5037 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5038 static const WCHAR szDisplayVersion[] =
5039 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5040 static const WCHAR szInstallSource[] =
5041 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5042 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5043 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5044 static const WCHAR szAuthorizedCDFPrefix[] =
5045 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5046 static const WCHAR szARPCONTACT[] =
5047 {'A','R','P','C','O','N','T','A','C','T',0};
5048 static const WCHAR szContact[] =
5049 {'C','o','n','t','a','c','t',0};
5050 static const WCHAR szARPCOMMENTS[] =
5051 {'A','R','P','C','O','M','M','E','N','T','S',0};
5052 static const WCHAR szComments[] =
5053 {'C','o','m','m','e','n','t','s',0};
5054 static const WCHAR szProductName[] =
5055 {'P','r','o','d','u','c','t','N','a','m','e',0};
5056 static const WCHAR szDisplayName[] =
5057 {'D','i','s','p','l','a','y','N','a','m','e',0};
5058 static const WCHAR szARPHELPLINK[] =
5059 {'A','R','P','H','E','L','P','L','I','N','K',0};
5060 static const WCHAR szHelpLink[] =
5061 {'H','e','l','p','L','i','n','k',0};
5062 static const WCHAR szARPHELPTELEPHONE[] =
5063 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5064 static const WCHAR szHelpTelephone[] =
5065 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5066 static const WCHAR szARPINSTALLLOCATION[] =
5067 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5068 static const WCHAR szManufacturer[] =
5069 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5070 static const WCHAR szPublisher[] =
5071 {'P','u','b','l','i','s','h','e','r',0};
5072 static const WCHAR szARPREADME[] =
5073 {'A','R','P','R','E','A','D','M','E',0};
5074 static const WCHAR szReadme[] =
5075 {'R','e','a','d','M','e',0};
5076 static const WCHAR szARPSIZE[] =
5077 {'A','R','P','S','I','Z','E',0};
5078 static const WCHAR szSize[] =
5079 {'S','i','z','e',0};
5080 static const WCHAR szARPURLINFOABOUT[] =
5081 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5082 static const WCHAR szURLInfoAbout[] =
5083 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5084 static const WCHAR szARPURLUPDATEINFO[] =
5085 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5086 static const WCHAR szURLUpdateInfo[] =
5087 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5088 static const WCHAR szARPSYSTEMCOMPONENT[] =
5089 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5090 static const WCHAR szSystemComponent[] =
5091 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5093 static const WCHAR *propval[] = {
5094 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5095 szARPCONTACT, szContact,
5096 szARPCOMMENTS, szComments,
5097 szProductName, szDisplayName,
5098 szARPHELPLINK, szHelpLink,
5099 szARPHELPTELEPHONE, szHelpTelephone,
5100 szARPINSTALLLOCATION, szInstallLocation,
5101 szSourceDir, szInstallSource,
5102 szManufacturer, szPublisher,
5103 szARPREADME, szReadme,
5104 szARPSIZE, szSize,
5105 szARPURLINFOABOUT, szURLInfoAbout,
5106 szARPURLUPDATEINFO, szURLUpdateInfo,
5107 NULL
5109 const WCHAR **p = propval;
5111 while (*p)
5113 prop = *p++;
5114 key = *p++;
5115 val = msi_dup_property(package->db, prop);
5116 msi_reg_set_val_str(hkey, key, val);
5117 msi_free(val);
5120 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5121 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5123 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5125 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5126 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5127 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5128 msi_free(buffer);
5130 /* FIXME: Write real Estimated Size when we have it */
5131 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5133 GetLocalTime(&systime);
5134 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5135 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5137 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5138 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5140 buffer = msi_dup_property(package->db, szProductVersion);
5141 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5142 if (buffer)
5144 DWORD verdword = msi_version_str_to_dword(buffer);
5146 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5147 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5148 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5149 msi_free(buffer);
5152 return ERROR_SUCCESS;
5155 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5157 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5158 MSIRECORD *uirow;
5159 LPWSTR upgrade_code;
5160 HKEY hkey, props, upgrade_key;
5161 UINT rc;
5163 /* FIXME: also need to publish if the product is in advertise mode */
5164 if (!msi_check_publish(package))
5165 return ERROR_SUCCESS;
5167 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5168 if (rc != ERROR_SUCCESS)
5169 return rc;
5171 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5172 if (rc != ERROR_SUCCESS)
5173 goto done;
5175 rc = msi_publish_install_properties(package, hkey);
5176 if (rc != ERROR_SUCCESS)
5177 goto done;
5179 rc = msi_publish_install_properties(package, props);
5180 if (rc != ERROR_SUCCESS)
5181 goto done;
5183 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5184 if (upgrade_code)
5186 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5187 if (rc == ERROR_SUCCESS)
5189 squash_guid( package->ProductCode, squashed_pc );
5190 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5191 RegCloseKey( upgrade_key );
5193 msi_free( upgrade_code );
5195 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5196 package->delete_on_close = FALSE;
5198 done:
5199 uirow = MSI_CreateRecord( 1 );
5200 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5201 msi_ui_actiondata( package, szRegisterProduct, uirow );
5202 msiobj_release( &uirow->hdr );
5204 RegCloseKey(hkey);
5205 return ERROR_SUCCESS;
5208 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5210 return execute_script(package, SCRIPT_INSTALL);
5213 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5215 MSIPACKAGE *package = param;
5216 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5217 WCHAR *p, *icon_path;
5219 if (!icon) return ERROR_SUCCESS;
5220 if ((icon_path = msi_build_icon_path( package, icon )))
5222 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5223 DeleteFileW( icon_path );
5224 if ((p = strrchrW( icon_path, '\\' )))
5226 *p = 0;
5227 RemoveDirectoryW( icon_path );
5229 msi_free( icon_path );
5231 return ERROR_SUCCESS;
5234 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5236 static const WCHAR query[]= {
5237 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5238 MSIQUERY *view;
5239 UINT r;
5241 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5242 if (r == ERROR_SUCCESS)
5244 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5245 msiobj_release( &view->hdr );
5246 if (r != ERROR_SUCCESS)
5247 return r;
5249 return ERROR_SUCCESS;
5252 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5254 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5255 WCHAR *upgrade, **features;
5256 BOOL full_uninstall = TRUE;
5257 MSIFEATURE *feature;
5258 MSIPATCHINFO *patch;
5259 UINT i;
5261 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5263 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5265 features = msi_split_string( remove, ',' );
5266 for (i = 0; features && features[i]; i++)
5268 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5270 msi_free(features);
5272 if (!full_uninstall)
5273 return ERROR_SUCCESS;
5275 MSIREG_DeleteProductKey(package->ProductCode);
5276 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5277 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5279 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5280 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5281 MSIREG_DeleteUserProductKey(package->ProductCode);
5282 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5284 upgrade = msi_dup_property(package->db, szUpgradeCode);
5285 if (upgrade)
5287 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5288 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5289 msi_free(upgrade);
5292 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5294 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5295 if (!strcmpW( package->ProductCode, patch->products ))
5297 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5298 patch->delete_on_close = TRUE;
5300 /* FIXME: remove local patch package if this is the last product */
5302 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5303 package->delete_on_close = TRUE;
5305 msi_unpublish_icons( package );
5306 return ERROR_SUCCESS;
5309 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5311 UINT rc;
5312 WCHAR *remove;
5314 /* turn off scheduling */
5315 package->script->CurrentlyScripting= FALSE;
5317 /* first do the same as an InstallExecute */
5318 rc = ACTION_InstallExecute(package);
5319 if (rc != ERROR_SUCCESS)
5320 return rc;
5322 /* then handle commit actions */
5323 rc = execute_script(package, SCRIPT_COMMIT);
5324 if (rc != ERROR_SUCCESS)
5325 return rc;
5327 remove = msi_dup_property(package->db, szRemove);
5328 rc = msi_unpublish_product(package, remove);
5329 msi_free(remove);
5330 return rc;
5333 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5335 static const WCHAR RunOnce[] = {
5336 'S','o','f','t','w','a','r','e','\\',
5337 'M','i','c','r','o','s','o','f','t','\\',
5338 'W','i','n','d','o','w','s','\\',
5339 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5340 'R','u','n','O','n','c','e',0};
5341 static const WCHAR InstallRunOnce[] = {
5342 'S','o','f','t','w','a','r','e','\\',
5343 'M','i','c','r','o','s','o','f','t','\\',
5344 'W','i','n','d','o','w','s','\\',
5345 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5346 'I','n','s','t','a','l','l','e','r','\\',
5347 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5349 static const WCHAR msiexec_fmt[] = {
5350 '%','s',
5351 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5352 '\"','%','s','\"',0};
5353 static const WCHAR install_fmt[] = {
5354 '/','I',' ','\"','%','s','\"',' ',
5355 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5356 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5357 WCHAR buffer[256], sysdir[MAX_PATH];
5358 HKEY hkey;
5359 WCHAR squished_pc[100];
5361 squash_guid(package->ProductCode,squished_pc);
5363 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5364 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5365 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5366 squished_pc);
5368 msi_reg_set_val_str( hkey, squished_pc, buffer );
5369 RegCloseKey(hkey);
5371 TRACE("Reboot command %s\n",debugstr_w(buffer));
5373 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5374 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5376 msi_reg_set_val_str( hkey, squished_pc, buffer );
5377 RegCloseKey(hkey);
5379 return ERROR_INSTALL_SUSPEND;
5382 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5384 static const WCHAR query[] =
5385 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5386 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5387 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5388 MSIRECORD *rec, *row;
5389 DWORD i, size = 0;
5390 va_list va;
5391 const WCHAR *str;
5392 WCHAR *data;
5394 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5396 rec = MSI_CreateRecord( count + 2 );
5397 str = MSI_RecordGetString( row, 1 );
5398 MSI_RecordSetStringW( rec, 0, str );
5399 msiobj_release( &row->hdr );
5400 MSI_RecordSetInteger( rec, 1, error );
5402 va_start( va, count );
5403 for (i = 0; i < count; i++)
5405 str = va_arg( va, const WCHAR *);
5406 MSI_RecordSetStringW( rec, i + 2, str );
5408 va_end( va );
5410 MSI_FormatRecordW( package, rec, NULL, &size );
5411 size++;
5412 data = msi_alloc( size * sizeof(WCHAR) );
5413 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5414 else data[0] = 0;
5415 msiobj_release( &rec->hdr );
5416 return data;
5419 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5421 DWORD attrib;
5422 UINT rc;
5425 * We are currently doing what should be done here in the top level Install
5426 * however for Administrative and uninstalls this step will be needed
5428 if (!package->PackagePath)
5429 return ERROR_SUCCESS;
5431 msi_set_sourcedir_props(package, TRUE);
5433 attrib = GetFileAttributesW(package->db->path);
5434 if (attrib == INVALID_FILE_ATTRIBUTES)
5436 LPWSTR prompt, msg;
5437 DWORD size = 0;
5439 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5440 package->Context, MSICODE_PRODUCT,
5441 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5442 if (rc == ERROR_MORE_DATA)
5444 prompt = msi_alloc(size * sizeof(WCHAR));
5445 MsiSourceListGetInfoW(package->ProductCode, NULL,
5446 package->Context, MSICODE_PRODUCT,
5447 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5449 else
5450 prompt = strdupW(package->db->path);
5452 msg = msi_build_error_string(package, 1302, 1, prompt);
5453 msi_free(prompt);
5454 while(attrib == INVALID_FILE_ATTRIBUTES)
5456 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5457 if (rc == IDCANCEL)
5459 msi_free(msg);
5460 return ERROR_INSTALL_USEREXIT;
5462 attrib = GetFileAttributesW(package->db->path);
5464 msi_free(msg);
5465 rc = ERROR_SUCCESS;
5467 else
5468 return ERROR_SUCCESS;
5470 return rc;
5473 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5475 HKEY hkey = 0;
5476 LPWSTR buffer, productid = NULL;
5477 UINT i, rc = ERROR_SUCCESS;
5478 MSIRECORD *uirow;
5480 static const WCHAR szPropKeys[][80] =
5482 {'P','r','o','d','u','c','t','I','D',0},
5483 {'U','S','E','R','N','A','M','E',0},
5484 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5485 {0},
5488 static const WCHAR szRegKeys[][80] =
5490 {'P','r','o','d','u','c','t','I','D',0},
5491 {'R','e','g','O','w','n','e','r',0},
5492 {'R','e','g','C','o','m','p','a','n','y',0},
5493 {0},
5496 if (msi_check_unpublish(package))
5498 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5499 goto end;
5502 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5503 if (!productid)
5504 goto end;
5506 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5507 NULL, &hkey, TRUE);
5508 if (rc != ERROR_SUCCESS)
5509 goto end;
5511 for( i = 0; szPropKeys[i][0]; i++ )
5513 buffer = msi_dup_property( package->db, szPropKeys[i] );
5514 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5515 msi_free( buffer );
5518 end:
5519 uirow = MSI_CreateRecord( 1 );
5520 MSI_RecordSetStringW( uirow, 1, productid );
5521 msi_ui_actiondata( package, szRegisterUser, uirow );
5522 msiobj_release( &uirow->hdr );
5524 msi_free(productid);
5525 RegCloseKey(hkey);
5526 return rc;
5530 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5532 UINT rc;
5534 package->script->InWhatSequence |= SEQUENCE_EXEC;
5535 rc = ACTION_ProcessExecSequence(package,FALSE);
5536 return rc;
5539 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5541 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5542 WCHAR productid_85[21], component_85[21], *ret;
5543 GUID clsid;
5544 DWORD sz;
5546 /* > is used if there is a component GUID and < if not. */
5548 productid_85[0] = 0;
5549 component_85[0] = 0;
5550 CLSIDFromString( package->ProductCode, &clsid );
5552 encode_base85_guid( &clsid, productid_85 );
5553 if (component)
5555 CLSIDFromString( component->ComponentId, &clsid );
5556 encode_base85_guid( &clsid, component_85 );
5559 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5560 debugstr_w(component_85));
5562 sz = 20 + strlenW( feature ) + 20 + 3;
5563 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5564 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5565 return ret;
5568 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5570 MSIPACKAGE *package = param;
5571 LPCWSTR compgroupid, component, feature, qualifier, text;
5572 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5573 HKEY hkey = NULL;
5574 UINT rc;
5575 MSICOMPONENT *comp;
5576 MSIFEATURE *feat;
5577 DWORD sz;
5578 MSIRECORD *uirow;
5579 int len;
5581 feature = MSI_RecordGetString(rec, 5);
5582 feat = msi_get_loaded_feature(package, feature);
5583 if (!feat)
5584 return ERROR_SUCCESS;
5586 feat->Action = msi_get_feature_action( package, feat );
5587 if (feat->Action != INSTALLSTATE_LOCAL &&
5588 feat->Action != INSTALLSTATE_SOURCE &&
5589 feat->Action != INSTALLSTATE_ADVERTISED)
5591 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5592 return ERROR_SUCCESS;
5595 component = MSI_RecordGetString(rec, 3);
5596 comp = msi_get_loaded_component(package, component);
5597 if (!comp)
5598 return ERROR_SUCCESS;
5600 compgroupid = MSI_RecordGetString(rec,1);
5601 qualifier = MSI_RecordGetString(rec,2);
5603 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5604 if (rc != ERROR_SUCCESS)
5605 goto end;
5607 advertise = msi_create_component_advertise_string( package, comp, feature );
5608 text = MSI_RecordGetString( rec, 4 );
5609 if (text)
5611 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5612 strcpyW( p, advertise );
5613 strcatW( p, text );
5614 msi_free( advertise );
5615 advertise = p;
5617 existing = msi_reg_get_val_str( hkey, qualifier );
5619 sz = strlenW( advertise ) + 1;
5620 if (existing)
5622 for (p = existing; *p; p += len)
5624 len = strlenW( p ) + 1;
5625 if (strcmpW( advertise, p )) sz += len;
5628 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5630 rc = ERROR_OUTOFMEMORY;
5631 goto end;
5633 q = output;
5634 if (existing)
5636 for (p = existing; *p; p += len)
5638 len = strlenW( p ) + 1;
5639 if (strcmpW( advertise, p ))
5641 memcpy( q, p, len * sizeof(WCHAR) );
5642 q += len;
5646 strcpyW( q, advertise );
5647 q[strlenW( q ) + 1] = 0;
5649 msi_reg_set_val_multi_str( hkey, qualifier, output );
5651 end:
5652 RegCloseKey(hkey);
5653 msi_free( output );
5654 msi_free( advertise );
5655 msi_free( existing );
5657 /* the UI chunk */
5658 uirow = MSI_CreateRecord( 2 );
5659 MSI_RecordSetStringW( uirow, 1, compgroupid );
5660 MSI_RecordSetStringW( uirow, 2, qualifier);
5661 msi_ui_actiondata( package, szPublishComponents, uirow );
5662 msiobj_release( &uirow->hdr );
5663 /* FIXME: call ui_progress? */
5665 return rc;
5669 * At present I am ignorning the advertised components part of this and only
5670 * focusing on the qualified component sets
5672 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5674 static const WCHAR query[] = {
5675 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5676 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5677 MSIQUERY *view;
5678 UINT rc;
5680 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5681 if (rc != ERROR_SUCCESS)
5682 return ERROR_SUCCESS;
5684 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5685 msiobj_release(&view->hdr);
5686 return rc;
5689 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5691 static const WCHAR szInstallerComponents[] = {
5692 'S','o','f','t','w','a','r','e','\\',
5693 'M','i','c','r','o','s','o','f','t','\\',
5694 'I','n','s','t','a','l','l','e','r','\\',
5695 'C','o','m','p','o','n','e','n','t','s','\\',0};
5697 MSIPACKAGE *package = param;
5698 LPCWSTR compgroupid, component, feature, qualifier;
5699 MSICOMPONENT *comp;
5700 MSIFEATURE *feat;
5701 MSIRECORD *uirow;
5702 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5703 LONG res;
5705 feature = MSI_RecordGetString( rec, 5 );
5706 feat = msi_get_loaded_feature( package, feature );
5707 if (!feat)
5708 return ERROR_SUCCESS;
5710 feat->Action = msi_get_feature_action( package, feat );
5711 if (feat->Action != INSTALLSTATE_ABSENT)
5713 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5714 return ERROR_SUCCESS;
5717 component = MSI_RecordGetString( rec, 3 );
5718 comp = msi_get_loaded_component( package, component );
5719 if (!comp)
5720 return ERROR_SUCCESS;
5722 compgroupid = MSI_RecordGetString( rec, 1 );
5723 qualifier = MSI_RecordGetString( rec, 2 );
5725 squash_guid( compgroupid, squashed );
5726 strcpyW( keypath, szInstallerComponents );
5727 strcatW( keypath, squashed );
5729 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5730 if (res != ERROR_SUCCESS)
5732 WARN("Unable to delete component key %d\n", res);
5735 uirow = MSI_CreateRecord( 2 );
5736 MSI_RecordSetStringW( uirow, 1, compgroupid );
5737 MSI_RecordSetStringW( uirow, 2, qualifier );
5738 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5739 msiobj_release( &uirow->hdr );
5741 return ERROR_SUCCESS;
5744 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5746 static const WCHAR query[] = {
5747 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5748 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5749 MSIQUERY *view;
5750 UINT rc;
5752 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5753 if (rc != ERROR_SUCCESS)
5754 return ERROR_SUCCESS;
5756 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5757 msiobj_release( &view->hdr );
5758 return rc;
5761 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5763 static const WCHAR query[] =
5764 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5765 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5766 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5767 MSIPACKAGE *package = param;
5768 MSICOMPONENT *component;
5769 MSIRECORD *row;
5770 MSIFILE *file;
5771 SC_HANDLE hscm = NULL, service = NULL;
5772 LPCWSTR comp, key;
5773 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5774 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5775 DWORD serv_type, start_type, err_control;
5776 SERVICE_DESCRIPTIONW sd = {NULL};
5777 UINT ret = ERROR_SUCCESS;
5779 comp = MSI_RecordGetString( rec, 12 );
5780 component = msi_get_loaded_component( package, comp );
5781 if (!component)
5783 WARN("service component not found\n");
5784 goto done;
5786 component->Action = msi_get_component_action( package, component );
5787 if (component->Action != INSTALLSTATE_LOCAL)
5789 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5790 goto done;
5792 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5793 if (!hscm)
5795 ERR("Failed to open the SC Manager!\n");
5796 goto done;
5799 start_type = MSI_RecordGetInteger(rec, 5);
5800 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5801 goto done;
5803 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5804 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5805 serv_type = MSI_RecordGetInteger(rec, 4);
5806 err_control = MSI_RecordGetInteger(rec, 6);
5807 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5808 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5809 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5810 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5811 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5812 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5814 /* fetch the service path */
5815 row = MSI_QueryGetRecord(package->db, query, comp);
5816 if (!row)
5818 ERR("Query failed\n");
5819 goto done;
5821 key = MSI_RecordGetString(row, 6);
5822 file = msi_get_loaded_file(package, key);
5823 msiobj_release(&row->hdr);
5824 if (!file)
5826 ERR("Failed to load the service file\n");
5827 goto done;
5830 if (!args || !args[0]) image_path = file->TargetPath;
5831 else
5833 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5834 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5836 ret = ERROR_OUTOFMEMORY;
5837 goto done;
5840 strcpyW(image_path, file->TargetPath);
5841 strcatW(image_path, szSpace);
5842 strcatW(image_path, args);
5844 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5845 start_type, err_control, image_path, load_order,
5846 NULL, depends, serv_name, pass);
5848 if (!service)
5850 if (GetLastError() != ERROR_SERVICE_EXISTS)
5851 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5853 else if (sd.lpDescription)
5855 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5856 WARN("failed to set service description %u\n", GetLastError());
5859 if (image_path != file->TargetPath) msi_free(image_path);
5860 done:
5861 CloseServiceHandle(service);
5862 CloseServiceHandle(hscm);
5863 msi_free(name);
5864 msi_free(disp);
5865 msi_free(sd.lpDescription);
5866 msi_free(load_order);
5867 msi_free(serv_name);
5868 msi_free(pass);
5869 msi_free(depends);
5870 msi_free(args);
5872 return ret;
5875 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5877 static const WCHAR query[] = {
5878 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5879 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5880 MSIQUERY *view;
5881 UINT rc;
5883 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5884 if (rc != ERROR_SUCCESS)
5885 return ERROR_SUCCESS;
5887 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5888 msiobj_release(&view->hdr);
5889 return rc;
5892 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5893 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5895 LPCWSTR *vector, *temp_vector;
5896 LPWSTR p, q;
5897 DWORD sep_len;
5899 static const WCHAR separator[] = {'[','~',']',0};
5901 *numargs = 0;
5902 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5904 if (!args)
5905 return NULL;
5907 vector = msi_alloc(sizeof(LPWSTR));
5908 if (!vector)
5909 return NULL;
5911 p = args;
5914 (*numargs)++;
5915 vector[*numargs - 1] = p;
5917 if ((q = strstrW(p, separator)))
5919 *q = '\0';
5921 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5922 if (!temp_vector)
5924 msi_free(vector);
5925 return NULL;
5927 vector = temp_vector;
5929 p = q + sep_len;
5931 } while (q);
5933 return vector;
5936 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5938 MSIPACKAGE *package = param;
5939 MSICOMPONENT *comp;
5940 MSIRECORD *uirow;
5941 SC_HANDLE scm = NULL, service = NULL;
5942 LPCWSTR component, *vector = NULL;
5943 LPWSTR name, args, display_name = NULL;
5944 DWORD event, numargs, len, wait, dummy;
5945 UINT r = ERROR_FUNCTION_FAILED;
5946 SERVICE_STATUS_PROCESS status;
5947 ULONGLONG start_time;
5949 component = MSI_RecordGetString(rec, 6);
5950 comp = msi_get_loaded_component(package, component);
5951 if (!comp)
5952 return ERROR_SUCCESS;
5954 comp->Action = msi_get_component_action( package, comp );
5955 if (comp->Action != INSTALLSTATE_LOCAL)
5957 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5958 return ERROR_SUCCESS;
5961 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5962 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5963 event = MSI_RecordGetInteger(rec, 3);
5964 wait = MSI_RecordGetInteger(rec, 5);
5966 if (!(event & msidbServiceControlEventStart))
5968 r = ERROR_SUCCESS;
5969 goto done;
5972 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5973 if (!scm)
5975 ERR("Failed to open the service control manager\n");
5976 goto done;
5979 len = 0;
5980 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5981 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5983 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5984 GetServiceDisplayNameW( scm, name, display_name, &len );
5987 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5988 if (!service)
5990 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5991 goto done;
5994 vector = msi_service_args_to_vector(args, &numargs);
5996 if (!StartServiceW(service, numargs, vector) &&
5997 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5999 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6000 goto done;
6003 r = ERROR_SUCCESS;
6004 if (wait)
6006 /* wait for at most 30 seconds for the service to be up and running */
6007 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6008 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6010 TRACE("failed to query service status (%u)\n", GetLastError());
6011 goto done;
6013 start_time = GetTickCount64();
6014 while (status.dwCurrentState == SERVICE_START_PENDING)
6016 if (GetTickCount64() - start_time > 30000) break;
6017 Sleep(1000);
6018 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6019 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6021 TRACE("failed to query service status (%u)\n", GetLastError());
6022 goto done;
6025 if (status.dwCurrentState != SERVICE_RUNNING)
6027 WARN("service failed to start %u\n", status.dwCurrentState);
6028 r = ERROR_FUNCTION_FAILED;
6032 done:
6033 uirow = MSI_CreateRecord( 2 );
6034 MSI_RecordSetStringW( uirow, 1, display_name );
6035 MSI_RecordSetStringW( uirow, 2, name );
6036 msi_ui_actiondata( package, szStartServices, uirow );
6037 msiobj_release( &uirow->hdr );
6039 CloseServiceHandle(service);
6040 CloseServiceHandle(scm);
6042 msi_free(name);
6043 msi_free(args);
6044 msi_free(vector);
6045 msi_free(display_name);
6046 return r;
6049 static UINT ACTION_StartServices( MSIPACKAGE *package )
6051 static const WCHAR query[] = {
6052 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6053 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6054 MSIQUERY *view;
6055 UINT rc;
6057 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6058 if (rc != ERROR_SUCCESS)
6059 return ERROR_SUCCESS;
6061 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6062 msiobj_release(&view->hdr);
6063 return rc;
6066 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6068 DWORD i, needed, count;
6069 ENUM_SERVICE_STATUSW *dependencies;
6070 SERVICE_STATUS ss;
6071 SC_HANDLE depserv;
6072 BOOL stopped, ret = FALSE;
6074 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6075 0, &needed, &count))
6076 return TRUE;
6078 if (GetLastError() != ERROR_MORE_DATA)
6079 return FALSE;
6081 dependencies = msi_alloc(needed);
6082 if (!dependencies)
6083 return FALSE;
6085 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6086 needed, &needed, &count))
6087 goto done;
6089 for (i = 0; i < count; i++)
6091 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6092 SERVICE_STOP | SERVICE_QUERY_STATUS);
6093 if (!depserv)
6094 goto done;
6096 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6097 CloseServiceHandle(depserv);
6098 if (!stopped)
6099 goto done;
6102 ret = TRUE;
6104 done:
6105 msi_free(dependencies);
6106 return ret;
6109 static UINT stop_service( LPCWSTR name )
6111 SC_HANDLE scm = NULL, service = NULL;
6112 SERVICE_STATUS status;
6113 SERVICE_STATUS_PROCESS ssp;
6114 DWORD needed;
6116 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6117 if (!scm)
6119 WARN("Failed to open the SCM: %d\n", GetLastError());
6120 goto done;
6123 service = OpenServiceW(scm, name,
6124 SERVICE_STOP |
6125 SERVICE_QUERY_STATUS |
6126 SERVICE_ENUMERATE_DEPENDENTS);
6127 if (!service)
6129 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6130 goto done;
6133 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6134 sizeof(SERVICE_STATUS_PROCESS), &needed))
6136 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6137 goto done;
6140 if (ssp.dwCurrentState == SERVICE_STOPPED)
6141 goto done;
6143 stop_service_dependents(scm, service);
6145 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6146 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6148 done:
6149 CloseServiceHandle(service);
6150 CloseServiceHandle(scm);
6152 return ERROR_SUCCESS;
6155 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6157 MSIPACKAGE *package = param;
6158 MSICOMPONENT *comp;
6159 MSIRECORD *uirow;
6160 LPCWSTR component;
6161 LPWSTR name = NULL, display_name = NULL;
6162 DWORD event, len;
6163 SC_HANDLE scm;
6165 event = MSI_RecordGetInteger( rec, 3 );
6166 if (!(event & msidbServiceControlEventStop))
6167 return ERROR_SUCCESS;
6169 component = MSI_RecordGetString( rec, 6 );
6170 comp = msi_get_loaded_component( package, component );
6171 if (!comp)
6172 return ERROR_SUCCESS;
6174 comp->Action = msi_get_component_action( package, comp );
6175 if (comp->Action != INSTALLSTATE_ABSENT)
6177 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6178 return ERROR_SUCCESS;
6181 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6182 if (!scm)
6184 ERR("Failed to open the service control manager\n");
6185 goto done;
6188 len = 0;
6189 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6190 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6192 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6193 GetServiceDisplayNameW( scm, name, display_name, &len );
6195 CloseServiceHandle( scm );
6197 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6198 stop_service( name );
6200 done:
6201 uirow = MSI_CreateRecord( 2 );
6202 MSI_RecordSetStringW( uirow, 1, display_name );
6203 MSI_RecordSetStringW( uirow, 2, name );
6204 msi_ui_actiondata( package, szStopServices, uirow );
6205 msiobj_release( &uirow->hdr );
6207 msi_free( name );
6208 msi_free( display_name );
6209 return ERROR_SUCCESS;
6212 static UINT ACTION_StopServices( MSIPACKAGE *package )
6214 static const WCHAR query[] = {
6215 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6216 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6217 MSIQUERY *view;
6218 UINT rc;
6220 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6221 if (rc != ERROR_SUCCESS)
6222 return ERROR_SUCCESS;
6224 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6225 msiobj_release(&view->hdr);
6226 return rc;
6229 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6231 MSIPACKAGE *package = param;
6232 MSICOMPONENT *comp;
6233 MSIRECORD *uirow;
6234 LPWSTR name = NULL, display_name = NULL;
6235 DWORD event, len;
6236 SC_HANDLE scm = NULL, service = NULL;
6238 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6239 if (!comp)
6240 return ERROR_SUCCESS;
6242 event = MSI_RecordGetInteger( rec, 3 );
6243 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6245 comp->Action = msi_get_component_action( package, comp );
6246 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6247 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6249 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6250 msi_free( name );
6251 return ERROR_SUCCESS;
6253 stop_service( name );
6255 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6256 if (!scm)
6258 WARN("Failed to open the SCM: %d\n", GetLastError());
6259 goto done;
6262 len = 0;
6263 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6264 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6266 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6267 GetServiceDisplayNameW( scm, name, display_name, &len );
6270 service = OpenServiceW( scm, name, DELETE );
6271 if (!service)
6273 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6274 goto done;
6277 if (!DeleteService( service ))
6278 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6280 done:
6281 uirow = MSI_CreateRecord( 2 );
6282 MSI_RecordSetStringW( uirow, 1, display_name );
6283 MSI_RecordSetStringW( uirow, 2, name );
6284 msi_ui_actiondata( package, szDeleteServices, uirow );
6285 msiobj_release( &uirow->hdr );
6287 CloseServiceHandle( service );
6288 CloseServiceHandle( scm );
6289 msi_free( name );
6290 msi_free( display_name );
6292 return ERROR_SUCCESS;
6295 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6297 static const WCHAR query[] = {
6298 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6299 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6300 MSIQUERY *view;
6301 UINT rc;
6303 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6304 if (rc != ERROR_SUCCESS)
6305 return ERROR_SUCCESS;
6307 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6308 msiobj_release( &view->hdr );
6309 return rc;
6312 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6314 MSIPACKAGE *package = param;
6315 LPWSTR driver, driver_path, ptr;
6316 WCHAR outpath[MAX_PATH];
6317 MSIFILE *driver_file = NULL, *setup_file = NULL;
6318 MSICOMPONENT *comp;
6319 MSIRECORD *uirow;
6320 LPCWSTR desc, file_key, component;
6321 DWORD len, usage;
6322 UINT r = ERROR_SUCCESS;
6324 static const WCHAR driver_fmt[] = {
6325 'D','r','i','v','e','r','=','%','s',0};
6326 static const WCHAR setup_fmt[] = {
6327 'S','e','t','u','p','=','%','s',0};
6328 static const WCHAR usage_fmt[] = {
6329 'F','i','l','e','U','s','a','g','e','=','1',0};
6331 component = MSI_RecordGetString( rec, 2 );
6332 comp = msi_get_loaded_component( package, component );
6333 if (!comp)
6334 return ERROR_SUCCESS;
6336 comp->Action = msi_get_component_action( package, comp );
6337 if (comp->Action != INSTALLSTATE_LOCAL)
6339 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6340 return ERROR_SUCCESS;
6342 desc = MSI_RecordGetString(rec, 3);
6344 file_key = MSI_RecordGetString( rec, 4 );
6345 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6347 file_key = MSI_RecordGetString( rec, 5 );
6348 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6350 if (!driver_file)
6352 ERR("ODBC Driver entry not found!\n");
6353 return ERROR_FUNCTION_FAILED;
6356 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6357 if (setup_file)
6358 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6359 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6361 driver = msi_alloc(len * sizeof(WCHAR));
6362 if (!driver)
6363 return ERROR_OUTOFMEMORY;
6365 ptr = driver;
6366 lstrcpyW(ptr, desc);
6367 ptr += lstrlenW(ptr) + 1;
6369 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6370 ptr += len + 1;
6372 if (setup_file)
6374 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6375 ptr += len + 1;
6378 lstrcpyW(ptr, usage_fmt);
6379 ptr += lstrlenW(ptr) + 1;
6380 *ptr = '\0';
6382 if (!driver_file->TargetPath)
6384 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6385 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6387 driver_path = strdupW(driver_file->TargetPath);
6388 ptr = strrchrW(driver_path, '\\');
6389 if (ptr) *ptr = '\0';
6391 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6392 NULL, ODBC_INSTALL_COMPLETE, &usage))
6394 ERR("Failed to install SQL driver!\n");
6395 r = ERROR_FUNCTION_FAILED;
6398 uirow = MSI_CreateRecord( 5 );
6399 MSI_RecordSetStringW( uirow, 1, desc );
6400 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6401 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6402 msi_ui_actiondata( package, szInstallODBC, uirow );
6403 msiobj_release( &uirow->hdr );
6405 msi_free(driver);
6406 msi_free(driver_path);
6408 return r;
6411 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6413 MSIPACKAGE *package = param;
6414 LPWSTR translator, translator_path, ptr;
6415 WCHAR outpath[MAX_PATH];
6416 MSIFILE *translator_file = NULL, *setup_file = NULL;
6417 MSICOMPONENT *comp;
6418 MSIRECORD *uirow;
6419 LPCWSTR desc, file_key, component;
6420 DWORD len, usage;
6421 UINT r = ERROR_SUCCESS;
6423 static const WCHAR translator_fmt[] = {
6424 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6425 static const WCHAR setup_fmt[] = {
6426 'S','e','t','u','p','=','%','s',0};
6428 component = MSI_RecordGetString( rec, 2 );
6429 comp = msi_get_loaded_component( package, component );
6430 if (!comp)
6431 return ERROR_SUCCESS;
6433 comp->Action = msi_get_component_action( package, comp );
6434 if (comp->Action != INSTALLSTATE_LOCAL)
6436 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6437 return ERROR_SUCCESS;
6439 desc = MSI_RecordGetString(rec, 3);
6441 file_key = MSI_RecordGetString( rec, 4 );
6442 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6444 file_key = MSI_RecordGetString( rec, 5 );
6445 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6447 if (!translator_file)
6449 ERR("ODBC Translator entry not found!\n");
6450 return ERROR_FUNCTION_FAILED;
6453 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6454 if (setup_file)
6455 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6457 translator = msi_alloc(len * sizeof(WCHAR));
6458 if (!translator)
6459 return ERROR_OUTOFMEMORY;
6461 ptr = translator;
6462 lstrcpyW(ptr, desc);
6463 ptr += lstrlenW(ptr) + 1;
6465 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6466 ptr += len + 1;
6468 if (setup_file)
6470 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6471 ptr += len + 1;
6473 *ptr = '\0';
6475 translator_path = strdupW(translator_file->TargetPath);
6476 ptr = strrchrW(translator_path, '\\');
6477 if (ptr) *ptr = '\0';
6479 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6480 NULL, ODBC_INSTALL_COMPLETE, &usage))
6482 ERR("Failed to install SQL translator!\n");
6483 r = ERROR_FUNCTION_FAILED;
6486 uirow = MSI_CreateRecord( 5 );
6487 MSI_RecordSetStringW( uirow, 1, desc );
6488 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6489 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6490 msi_ui_actiondata( package, szInstallODBC, uirow );
6491 msiobj_release( &uirow->hdr );
6493 msi_free(translator);
6494 msi_free(translator_path);
6496 return r;
6499 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6501 MSIPACKAGE *package = param;
6502 MSICOMPONENT *comp;
6503 LPWSTR attrs;
6504 LPCWSTR desc, driver, component;
6505 WORD request = ODBC_ADD_SYS_DSN;
6506 INT registration;
6507 DWORD len;
6508 UINT r = ERROR_SUCCESS;
6509 MSIRECORD *uirow;
6511 static const WCHAR attrs_fmt[] = {
6512 'D','S','N','=','%','s',0 };
6514 component = MSI_RecordGetString( rec, 2 );
6515 comp = msi_get_loaded_component( package, component );
6516 if (!comp)
6517 return ERROR_SUCCESS;
6519 comp->Action = msi_get_component_action( package, comp );
6520 if (comp->Action != INSTALLSTATE_LOCAL)
6522 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6523 return ERROR_SUCCESS;
6526 desc = MSI_RecordGetString(rec, 3);
6527 driver = MSI_RecordGetString(rec, 4);
6528 registration = MSI_RecordGetInteger(rec, 5);
6530 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6531 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6533 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6534 attrs = msi_alloc(len * sizeof(WCHAR));
6535 if (!attrs)
6536 return ERROR_OUTOFMEMORY;
6538 len = sprintfW(attrs, attrs_fmt, desc);
6539 attrs[len + 1] = 0;
6541 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6543 ERR("Failed to install SQL data source!\n");
6544 r = ERROR_FUNCTION_FAILED;
6547 uirow = MSI_CreateRecord( 5 );
6548 MSI_RecordSetStringW( uirow, 1, desc );
6549 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6550 MSI_RecordSetInteger( uirow, 3, request );
6551 msi_ui_actiondata( package, szInstallODBC, uirow );
6552 msiobj_release( &uirow->hdr );
6554 msi_free(attrs);
6556 return r;
6559 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6561 static const WCHAR driver_query[] = {
6562 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6563 'O','D','B','C','D','r','i','v','e','r',0};
6564 static const WCHAR translator_query[] = {
6565 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6566 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6567 static const WCHAR source_query[] = {
6568 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6569 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6570 MSIQUERY *view;
6571 UINT rc;
6573 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6574 if (rc == ERROR_SUCCESS)
6576 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6577 msiobj_release(&view->hdr);
6578 if (rc != ERROR_SUCCESS)
6579 return rc;
6581 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6582 if (rc == ERROR_SUCCESS)
6584 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6585 msiobj_release(&view->hdr);
6586 if (rc != ERROR_SUCCESS)
6587 return rc;
6589 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6590 if (rc == ERROR_SUCCESS)
6592 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6593 msiobj_release(&view->hdr);
6594 if (rc != ERROR_SUCCESS)
6595 return rc;
6597 return ERROR_SUCCESS;
6600 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6602 MSIPACKAGE *package = param;
6603 MSICOMPONENT *comp;
6604 MSIRECORD *uirow;
6605 DWORD usage;
6606 LPCWSTR desc, component;
6608 component = MSI_RecordGetString( rec, 2 );
6609 comp = msi_get_loaded_component( package, component );
6610 if (!comp)
6611 return ERROR_SUCCESS;
6613 comp->Action = msi_get_component_action( package, comp );
6614 if (comp->Action != INSTALLSTATE_ABSENT)
6616 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6617 return ERROR_SUCCESS;
6620 desc = MSI_RecordGetString( rec, 3 );
6621 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6623 WARN("Failed to remove ODBC driver\n");
6625 else if (!usage)
6627 FIXME("Usage count reached 0\n");
6630 uirow = MSI_CreateRecord( 2 );
6631 MSI_RecordSetStringW( uirow, 1, desc );
6632 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6633 msi_ui_actiondata( package, szRemoveODBC, uirow );
6634 msiobj_release( &uirow->hdr );
6636 return ERROR_SUCCESS;
6639 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6641 MSIPACKAGE *package = param;
6642 MSICOMPONENT *comp;
6643 MSIRECORD *uirow;
6644 DWORD usage;
6645 LPCWSTR desc, component;
6647 component = MSI_RecordGetString( rec, 2 );
6648 comp = msi_get_loaded_component( package, component );
6649 if (!comp)
6650 return ERROR_SUCCESS;
6652 comp->Action = msi_get_component_action( package, comp );
6653 if (comp->Action != INSTALLSTATE_ABSENT)
6655 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6656 return ERROR_SUCCESS;
6659 desc = MSI_RecordGetString( rec, 3 );
6660 if (!SQLRemoveTranslatorW( desc, &usage ))
6662 WARN("Failed to remove ODBC translator\n");
6664 else if (!usage)
6666 FIXME("Usage count reached 0\n");
6669 uirow = MSI_CreateRecord( 2 );
6670 MSI_RecordSetStringW( uirow, 1, desc );
6671 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6672 msi_ui_actiondata( package, szRemoveODBC, uirow );
6673 msiobj_release( &uirow->hdr );
6675 return ERROR_SUCCESS;
6678 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6680 MSIPACKAGE *package = param;
6681 MSICOMPONENT *comp;
6682 MSIRECORD *uirow;
6683 LPWSTR attrs;
6684 LPCWSTR desc, driver, component;
6685 WORD request = ODBC_REMOVE_SYS_DSN;
6686 INT registration;
6687 DWORD len;
6689 static const WCHAR attrs_fmt[] = {
6690 'D','S','N','=','%','s',0 };
6692 component = MSI_RecordGetString( rec, 2 );
6693 comp = msi_get_loaded_component( package, component );
6694 if (!comp)
6695 return ERROR_SUCCESS;
6697 comp->Action = msi_get_component_action( package, comp );
6698 if (comp->Action != INSTALLSTATE_ABSENT)
6700 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6701 return ERROR_SUCCESS;
6704 desc = MSI_RecordGetString( rec, 3 );
6705 driver = MSI_RecordGetString( rec, 4 );
6706 registration = MSI_RecordGetInteger( rec, 5 );
6708 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6709 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6711 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6712 attrs = msi_alloc( len * sizeof(WCHAR) );
6713 if (!attrs)
6714 return ERROR_OUTOFMEMORY;
6716 FIXME("Use ODBCSourceAttribute table\n");
6718 len = sprintfW( attrs, attrs_fmt, desc );
6719 attrs[len + 1] = 0;
6721 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6723 WARN("Failed to remove ODBC data source\n");
6725 msi_free( attrs );
6727 uirow = MSI_CreateRecord( 3 );
6728 MSI_RecordSetStringW( uirow, 1, desc );
6729 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6730 MSI_RecordSetInteger( uirow, 3, request );
6731 msi_ui_actiondata( package, szRemoveODBC, uirow );
6732 msiobj_release( &uirow->hdr );
6734 return ERROR_SUCCESS;
6737 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6739 static const WCHAR driver_query[] = {
6740 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6741 'O','D','B','C','D','r','i','v','e','r',0};
6742 static const WCHAR translator_query[] = {
6743 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6744 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6745 static const WCHAR source_query[] = {
6746 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6747 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6748 MSIQUERY *view;
6749 UINT rc;
6751 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6752 if (rc == ERROR_SUCCESS)
6754 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6755 msiobj_release( &view->hdr );
6756 if (rc != ERROR_SUCCESS)
6757 return rc;
6759 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6760 if (rc == ERROR_SUCCESS)
6762 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6763 msiobj_release( &view->hdr );
6764 if (rc != ERROR_SUCCESS)
6765 return rc;
6767 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6768 if (rc == ERROR_SUCCESS)
6770 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6771 msiobj_release( &view->hdr );
6772 if (rc != ERROR_SUCCESS)
6773 return rc;
6775 return ERROR_SUCCESS;
6778 #define ENV_ACT_SETALWAYS 0x1
6779 #define ENV_ACT_SETABSENT 0x2
6780 #define ENV_ACT_REMOVE 0x4
6781 #define ENV_ACT_REMOVEMATCH 0x8
6783 #define ENV_MOD_MACHINE 0x20000000
6784 #define ENV_MOD_APPEND 0x40000000
6785 #define ENV_MOD_PREFIX 0x80000000
6786 #define ENV_MOD_MASK 0xC0000000
6788 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6790 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6792 LPCWSTR cptr = *name;
6794 static const WCHAR prefix[] = {'[','~',']',0};
6795 static const int prefix_len = 3;
6797 *flags = 0;
6798 while (*cptr)
6800 if (*cptr == '=')
6801 *flags |= ENV_ACT_SETALWAYS;
6802 else if (*cptr == '+')
6803 *flags |= ENV_ACT_SETABSENT;
6804 else if (*cptr == '-')
6805 *flags |= ENV_ACT_REMOVE;
6806 else if (*cptr == '!')
6807 *flags |= ENV_ACT_REMOVEMATCH;
6808 else if (*cptr == '*')
6809 *flags |= ENV_MOD_MACHINE;
6810 else
6811 break;
6813 cptr++;
6814 (*name)++;
6817 if (!*cptr)
6819 ERR("Missing environment variable\n");
6820 return ERROR_FUNCTION_FAILED;
6823 if (*value)
6825 LPCWSTR ptr = *value;
6826 if (!strncmpW(ptr, prefix, prefix_len))
6828 if (ptr[prefix_len] == szSemiColon[0])
6830 *flags |= ENV_MOD_APPEND;
6831 *value += lstrlenW(prefix);
6833 else
6835 *value = NULL;
6838 else if (lstrlenW(*value) >= prefix_len)
6840 ptr += lstrlenW(ptr) - prefix_len;
6841 if (!strcmpW( ptr, prefix ))
6843 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6845 *flags |= ENV_MOD_PREFIX;
6846 /* the "[~]" will be removed by deformat_string */;
6848 else
6850 *value = NULL;
6856 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6857 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6858 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6859 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6861 ERR("Invalid flags: %08x\n", *flags);
6862 return ERROR_FUNCTION_FAILED;
6865 if (!*flags)
6866 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6868 return ERROR_SUCCESS;
6871 static UINT open_env_key( DWORD flags, HKEY *key )
6873 static const WCHAR user_env[] =
6874 {'E','n','v','i','r','o','n','m','e','n','t',0};
6875 static const WCHAR machine_env[] =
6876 {'S','y','s','t','e','m','\\',
6877 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6878 'C','o','n','t','r','o','l','\\',
6879 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6880 'E','n','v','i','r','o','n','m','e','n','t',0};
6881 const WCHAR *env;
6882 HKEY root;
6883 LONG res;
6885 if (flags & ENV_MOD_MACHINE)
6887 env = machine_env;
6888 root = HKEY_LOCAL_MACHINE;
6890 else
6892 env = user_env;
6893 root = HKEY_CURRENT_USER;
6896 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6897 if (res != ERROR_SUCCESS)
6899 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6900 return ERROR_FUNCTION_FAILED;
6903 return ERROR_SUCCESS;
6906 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6908 MSIPACKAGE *package = param;
6909 LPCWSTR name, value, component;
6910 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6911 DWORD flags, type, size;
6912 UINT res;
6913 HKEY env = NULL;
6914 MSICOMPONENT *comp;
6915 MSIRECORD *uirow;
6916 int action = 0;
6918 component = MSI_RecordGetString(rec, 4);
6919 comp = msi_get_loaded_component(package, component);
6920 if (!comp)
6921 return ERROR_SUCCESS;
6923 comp->Action = msi_get_component_action( package, comp );
6924 if (comp->Action != INSTALLSTATE_LOCAL)
6926 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6927 return ERROR_SUCCESS;
6929 name = MSI_RecordGetString(rec, 2);
6930 value = MSI_RecordGetString(rec, 3);
6932 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6934 res = env_parse_flags(&name, &value, &flags);
6935 if (res != ERROR_SUCCESS || !value)
6936 goto done;
6938 if (value && !deformat_string(package, value, &deformatted))
6940 res = ERROR_OUTOFMEMORY;
6941 goto done;
6944 value = deformatted;
6946 res = open_env_key( flags, &env );
6947 if (res != ERROR_SUCCESS)
6948 goto done;
6950 if (flags & ENV_MOD_MACHINE)
6951 action |= 0x20000000;
6953 size = 0;
6954 type = REG_SZ;
6955 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6956 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6957 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6958 goto done;
6960 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6962 action = 0x2;
6964 /* Nothing to do. */
6965 if (!value)
6967 res = ERROR_SUCCESS;
6968 goto done;
6971 /* If we are appending but the string was empty, strip ; */
6972 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6974 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6975 newval = strdupW(value);
6976 if (!newval)
6978 res = ERROR_OUTOFMEMORY;
6979 goto done;
6982 else
6984 action = 0x1;
6986 /* Contrary to MSDN, +-variable to [~];path works */
6987 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6989 res = ERROR_SUCCESS;
6990 goto done;
6993 data = msi_alloc(size);
6994 if (!data)
6996 RegCloseKey(env);
6997 return ERROR_OUTOFMEMORY;
7000 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7001 if (res != ERROR_SUCCESS)
7002 goto done;
7004 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7006 action = 0x4;
7007 res = RegDeleteValueW(env, name);
7008 if (res != ERROR_SUCCESS)
7009 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7010 goto done;
7013 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7014 if (flags & ENV_MOD_MASK)
7016 DWORD mod_size;
7017 int multiplier = 0;
7018 if (flags & ENV_MOD_APPEND) multiplier++;
7019 if (flags & ENV_MOD_PREFIX) multiplier++;
7020 mod_size = lstrlenW(value) * multiplier;
7021 size += mod_size * sizeof(WCHAR);
7024 newval = msi_alloc(size);
7025 ptr = newval;
7026 if (!newval)
7028 res = ERROR_OUTOFMEMORY;
7029 goto done;
7032 if (flags & ENV_MOD_PREFIX)
7034 lstrcpyW(newval, value);
7035 ptr = newval + lstrlenW(value);
7036 action |= 0x80000000;
7039 lstrcpyW(ptr, data);
7041 if (flags & ENV_MOD_APPEND)
7043 lstrcatW(newval, value);
7044 action |= 0x40000000;
7047 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7048 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7049 if (res)
7051 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7054 done:
7055 uirow = MSI_CreateRecord( 3 );
7056 MSI_RecordSetStringW( uirow, 1, name );
7057 MSI_RecordSetStringW( uirow, 2, newval );
7058 MSI_RecordSetInteger( uirow, 3, action );
7059 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7060 msiobj_release( &uirow->hdr );
7062 if (env) RegCloseKey(env);
7063 msi_free(deformatted);
7064 msi_free(data);
7065 msi_free(newval);
7066 return res;
7069 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7071 static const WCHAR query[] = {
7072 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7073 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7074 MSIQUERY *view;
7075 UINT rc;
7077 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7078 if (rc != ERROR_SUCCESS)
7079 return ERROR_SUCCESS;
7081 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7082 msiobj_release(&view->hdr);
7083 return rc;
7086 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7088 MSIPACKAGE *package = param;
7089 LPCWSTR name, value, component;
7090 LPWSTR deformatted = NULL;
7091 DWORD flags;
7092 HKEY env;
7093 MSICOMPONENT *comp;
7094 MSIRECORD *uirow;
7095 int action = 0;
7096 LONG res;
7097 UINT r;
7099 component = MSI_RecordGetString( rec, 4 );
7100 comp = msi_get_loaded_component( package, component );
7101 if (!comp)
7102 return ERROR_SUCCESS;
7104 comp->Action = msi_get_component_action( package, comp );
7105 if (comp->Action != INSTALLSTATE_ABSENT)
7107 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7108 return ERROR_SUCCESS;
7110 name = MSI_RecordGetString( rec, 2 );
7111 value = MSI_RecordGetString( rec, 3 );
7113 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7115 r = env_parse_flags( &name, &value, &flags );
7116 if (r != ERROR_SUCCESS)
7117 return r;
7119 if (!(flags & ENV_ACT_REMOVE))
7121 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7122 return ERROR_SUCCESS;
7125 if (value && !deformat_string( package, value, &deformatted ))
7126 return ERROR_OUTOFMEMORY;
7128 value = deformatted;
7130 r = open_env_key( flags, &env );
7131 if (r != ERROR_SUCCESS)
7133 r = ERROR_SUCCESS;
7134 goto done;
7137 if (flags & ENV_MOD_MACHINE)
7138 action |= 0x20000000;
7140 TRACE("Removing %s\n", debugstr_w(name));
7142 res = RegDeleteValueW( env, name );
7143 if (res != ERROR_SUCCESS)
7145 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7146 r = ERROR_SUCCESS;
7149 done:
7150 uirow = MSI_CreateRecord( 3 );
7151 MSI_RecordSetStringW( uirow, 1, name );
7152 MSI_RecordSetStringW( uirow, 2, value );
7153 MSI_RecordSetInteger( uirow, 3, action );
7154 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7155 msiobj_release( &uirow->hdr );
7157 if (env) RegCloseKey( env );
7158 msi_free( deformatted );
7159 return r;
7162 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7164 static const WCHAR query[] = {
7165 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7166 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7167 MSIQUERY *view;
7168 UINT rc;
7170 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7171 if (rc != ERROR_SUCCESS)
7172 return ERROR_SUCCESS;
7174 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7175 msiobj_release( &view->hdr );
7176 return rc;
7179 UINT msi_validate_product_id( MSIPACKAGE *package )
7181 LPWSTR key, template, id;
7182 UINT r = ERROR_SUCCESS;
7184 id = msi_dup_property( package->db, szProductID );
7185 if (id)
7187 msi_free( id );
7188 return ERROR_SUCCESS;
7190 template = msi_dup_property( package->db, szPIDTemplate );
7191 key = msi_dup_property( package->db, szPIDKEY );
7192 if (key && template)
7194 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7195 r = msi_set_property( package->db, szProductID, key, -1 );
7197 msi_free( template );
7198 msi_free( key );
7199 return r;
7202 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7204 return msi_validate_product_id( package );
7207 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7209 TRACE("\n");
7210 package->need_reboot_at_end = 1;
7211 return ERROR_SUCCESS;
7214 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7216 static const WCHAR szAvailableFreeReg[] =
7217 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7218 MSIRECORD *uirow;
7219 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7221 TRACE("%p %d kilobytes\n", package, space);
7223 uirow = MSI_CreateRecord( 1 );
7224 MSI_RecordSetInteger( uirow, 1, space );
7225 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7226 msiobj_release( &uirow->hdr );
7228 return ERROR_SUCCESS;
7231 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7233 TRACE("%p\n", package);
7235 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7236 return ERROR_SUCCESS;
7239 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7241 FIXME("%p\n", package);
7242 return ERROR_SUCCESS;
7245 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7247 static const WCHAR driver_query[] = {
7248 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7249 'O','D','B','C','D','r','i','v','e','r',0};
7250 static const WCHAR translator_query[] = {
7251 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7252 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7253 MSIQUERY *view;
7254 UINT r, count;
7256 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7257 if (r == ERROR_SUCCESS)
7259 count = 0;
7260 r = MSI_IterateRecords( view, &count, NULL, package );
7261 msiobj_release( &view->hdr );
7262 if (r != ERROR_SUCCESS)
7263 return r;
7264 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7266 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7267 if (r == ERROR_SUCCESS)
7269 count = 0;
7270 r = MSI_IterateRecords( view, &count, NULL, package );
7271 msiobj_release( &view->hdr );
7272 if (r != ERROR_SUCCESS)
7273 return r;
7274 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7276 return ERROR_SUCCESS;
7279 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7281 static const WCHAR fmtW[] =
7282 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7283 MSIPACKAGE *package = param;
7284 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7285 int attrs = MSI_RecordGetInteger( rec, 5 );
7286 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7287 WCHAR *product, *features, *cmd;
7288 STARTUPINFOW si;
7289 PROCESS_INFORMATION info;
7290 BOOL ret;
7292 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7293 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7295 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7297 len += strlenW( product );
7298 if (features)
7299 len += strlenW( features );
7300 else
7301 len += sizeof(szAll) / sizeof(szAll[0]);
7303 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7305 msi_free( product );
7306 msi_free( features );
7307 return ERROR_OUTOFMEMORY;
7309 sprintfW( cmd, fmtW, product, features ? features : szAll );
7310 msi_free( product );
7311 msi_free( features );
7313 memset( &si, 0, sizeof(STARTUPINFOW) );
7314 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7315 msi_free( cmd );
7316 if (!ret) return GetLastError();
7317 CloseHandle( info.hThread );
7319 WaitForSingleObject( info.hProcess, INFINITE );
7320 CloseHandle( info.hProcess );
7321 return ERROR_SUCCESS;
7324 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7326 static const WCHAR query[] = {
7327 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7328 MSIQUERY *view;
7329 UINT r;
7331 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7332 if (r == ERROR_SUCCESS)
7334 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7335 msiobj_release( &view->hdr );
7336 if (r != ERROR_SUCCESS)
7337 return r;
7339 return ERROR_SUCCESS;
7342 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7344 MSIPACKAGE *package = param;
7345 int attributes = MSI_RecordGetInteger( rec, 5 );
7347 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7349 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7350 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7351 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7352 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7353 HKEY hkey;
7354 UINT r;
7356 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7358 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7359 if (r != ERROR_SUCCESS)
7360 return ERROR_SUCCESS;
7362 else
7364 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7365 if (r != ERROR_SUCCESS)
7366 return ERROR_SUCCESS;
7368 RegCloseKey( hkey );
7370 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7371 debugstr_w(upgrade_code), debugstr_w(version_min),
7372 debugstr_w(version_max), debugstr_w(language));
7374 return ERROR_SUCCESS;
7377 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7379 static const WCHAR query[] = {
7380 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7381 'U','p','g','r','a','d','e',0};
7382 MSIQUERY *view;
7383 UINT r;
7385 if (msi_get_property_int( package->db, szInstalled, 0 ))
7387 TRACE("product is installed, skipping action\n");
7388 return ERROR_SUCCESS;
7390 if (msi_get_property_int( package->db, szPreselected, 0 ))
7392 TRACE("Preselected property is set, not migrating feature states\n");
7393 return ERROR_SUCCESS;
7395 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7396 if (r == ERROR_SUCCESS)
7398 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7399 msiobj_release( &view->hdr );
7400 if (r != ERROR_SUCCESS)
7401 return r;
7403 return ERROR_SUCCESS;
7406 static void bind_image( const char *filename, const char *path )
7408 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7410 WARN("failed to bind image %u\n", GetLastError());
7414 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7416 UINT i;
7417 MSIFILE *file;
7418 MSIPACKAGE *package = param;
7419 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7420 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7421 char *filenameA, *pathA;
7422 WCHAR *pathW, **path_list;
7424 if (!(file = msi_get_loaded_file( package, key )))
7426 WARN("file %s not found\n", debugstr_w(key));
7427 return ERROR_SUCCESS;
7429 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7430 path_list = msi_split_string( paths, ';' );
7431 if (!path_list) bind_image( filenameA, NULL );
7432 else
7434 for (i = 0; path_list[i] && path_list[i][0]; i++)
7436 deformat_string( package, path_list[i], &pathW );
7437 if ((pathA = strdupWtoA( pathW )))
7439 bind_image( filenameA, pathA );
7440 msi_free( pathA );
7442 msi_free( pathW );
7445 msi_free( path_list );
7446 msi_free( filenameA );
7447 return ERROR_SUCCESS;
7450 static UINT ACTION_BindImage( MSIPACKAGE *package )
7452 static const WCHAR query[] = {
7453 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7454 'B','i','n','d','I','m','a','g','e',0};
7455 MSIQUERY *view;
7456 UINT r;
7458 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7459 if (r == ERROR_SUCCESS)
7461 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7462 msiobj_release( &view->hdr );
7463 if (r != ERROR_SUCCESS)
7464 return r;
7466 return ERROR_SUCCESS;
7469 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7471 static const WCHAR query[] = {
7472 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7473 MSIQUERY *view;
7474 DWORD count = 0;
7475 UINT r;
7477 r = MSI_OpenQuery( package->db, &view, query, table );
7478 if (r == ERROR_SUCCESS)
7480 r = MSI_IterateRecords(view, &count, NULL, package);
7481 msiobj_release(&view->hdr);
7482 if (r != ERROR_SUCCESS)
7483 return r;
7485 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7486 return ERROR_SUCCESS;
7489 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7491 static const WCHAR table[] = {
7492 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7493 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7496 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7498 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7499 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7502 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7504 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7505 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7508 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7510 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7511 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7514 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7516 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7517 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7520 static const struct
7522 const WCHAR *action;
7523 UINT (*handler)(MSIPACKAGE *);
7524 const WCHAR *action_rollback;
7526 StandardActions[] =
7528 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7529 { szAppSearch, ACTION_AppSearch, NULL },
7530 { szBindImage, ACTION_BindImage, NULL },
7531 { szCCPSearch, ACTION_CCPSearch, NULL },
7532 { szCostFinalize, ACTION_CostFinalize, NULL },
7533 { szCostInitialize, ACTION_CostInitialize, NULL },
7534 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7535 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7536 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7537 { szDisableRollback, ACTION_DisableRollback, NULL },
7538 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7539 { szExecuteAction, ACTION_ExecuteAction, NULL },
7540 { szFileCost, ACTION_FileCost, NULL },
7541 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7542 { szForceReboot, ACTION_ForceReboot, NULL },
7543 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7544 { szInstallExecute, ACTION_InstallExecute, NULL },
7545 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7546 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7547 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7548 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7549 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7550 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7551 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7552 { szInstallValidate, ACTION_InstallValidate, NULL },
7553 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7554 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7555 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7556 { szMoveFiles, ACTION_MoveFiles, NULL },
7557 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7558 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7559 { szPatchFiles, ACTION_PatchFiles, NULL },
7560 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7561 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7562 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7563 { szPublishProduct, ACTION_PublishProduct, NULL },
7564 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7565 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7566 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7567 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7568 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7569 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7570 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7571 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7572 { szRegisterUser, ACTION_RegisterUser, NULL },
7573 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7574 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7575 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7576 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7577 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7578 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7579 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7580 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7581 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7582 { szResolveSource, ACTION_ResolveSource, NULL },
7583 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7584 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7585 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7586 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7587 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7588 { szStartServices, ACTION_StartServices, szStopServices },
7589 { szStopServices, ACTION_StopServices, szStartServices },
7590 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7591 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7592 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7593 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7594 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7595 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7596 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7597 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7598 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7599 { szValidateProductID, ACTION_ValidateProductID, NULL },
7600 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7601 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7602 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7603 { NULL, NULL, NULL }
7606 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7608 BOOL ret = FALSE;
7609 UINT i;
7611 i = 0;
7612 while (StandardActions[i].action != NULL)
7614 if (!strcmpW( StandardActions[i].action, action ))
7616 ui_actionstart( package, action );
7617 if (StandardActions[i].handler)
7619 ui_actioninfo( package, action, TRUE, 0 );
7620 *rc = StandardActions[i].handler( package );
7621 ui_actioninfo( package, action, FALSE, *rc );
7623 if (StandardActions[i].action_rollback && !package->need_rollback)
7625 TRACE("scheduling rollback action\n");
7626 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7629 else
7631 FIXME("unhandled standard action %s\n", debugstr_w(action));
7632 *rc = ERROR_SUCCESS;
7634 ret = TRUE;
7635 break;
7637 i++;
7639 return ret;
7642 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7644 UINT rc = ERROR_SUCCESS;
7645 BOOL handled;
7647 TRACE("Performing action (%s)\n", debugstr_w(action));
7649 handled = ACTION_HandleStandardAction(package, action, &rc);
7651 if (!handled)
7652 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7654 if (!handled)
7656 WARN("unhandled msi action %s\n", debugstr_w(action));
7657 rc = ERROR_FUNCTION_NOT_CALLED;
7660 return rc;
7663 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7665 UINT rc = ERROR_SUCCESS;
7666 BOOL handled = FALSE;
7668 TRACE("Performing action (%s)\n", debugstr_w(action));
7670 package->action_progress_increment = 0;
7671 handled = ACTION_HandleStandardAction(package, action, &rc);
7673 if (!handled)
7674 handled = ACTION_HandleCustomAction(package, action, &rc, script);
7676 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7677 handled = TRUE;
7679 if (!handled)
7681 WARN("unhandled msi action %s\n", debugstr_w(action));
7682 rc = ERROR_FUNCTION_NOT_CALLED;
7685 return rc;
7688 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7690 UINT rc = ERROR_SUCCESS;
7691 MSIRECORD *row;
7693 static const WCHAR query[] =
7694 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7695 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7696 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7697 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7698 static const WCHAR ui_query[] =
7699 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7700 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7701 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7702 ' ', '=',' ','%','i',0};
7704 if (needs_ui_sequence(package))
7705 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7706 else
7707 row = MSI_QueryGetRecord(package->db, query, seq);
7709 if (row)
7711 LPCWSTR action, cond;
7713 TRACE("Running the actions\n");
7715 /* check conditions */
7716 cond = MSI_RecordGetString(row, 2);
7718 /* this is a hack to skip errors in the condition code */
7719 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7721 msiobj_release(&row->hdr);
7722 return ERROR_SUCCESS;
7725 action = MSI_RecordGetString(row, 1);
7726 if (!action)
7728 ERR("failed to fetch action\n");
7729 msiobj_release(&row->hdr);
7730 return ERROR_FUNCTION_FAILED;
7733 if (needs_ui_sequence(package))
7734 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7735 else
7736 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7738 msiobj_release(&row->hdr);
7741 return rc;
7744 /****************************************************
7745 * TOP level entry points
7746 *****************************************************/
7748 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7749 LPCWSTR szCommandLine )
7751 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7752 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7753 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7754 WCHAR *reinstall, *remove, *patch;
7755 BOOL ui_exists;
7756 UINT rc;
7758 msi_set_property( package->db, szAction, szInstall, -1 );
7760 package->script->InWhatSequence = SEQUENCE_INSTALL;
7762 if (szPackagePath)
7764 LPWSTR p, dir;
7765 LPCWSTR file;
7767 dir = strdupW(szPackagePath);
7768 p = strrchrW(dir, '\\');
7769 if (p)
7771 *(++p) = 0;
7772 file = szPackagePath + (p - dir);
7774 else
7776 msi_free(dir);
7777 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7778 GetCurrentDirectoryW(MAX_PATH, dir);
7779 lstrcatW(dir, szBackSlash);
7780 file = szPackagePath;
7783 msi_free( package->PackagePath );
7784 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7785 if (!package->PackagePath)
7787 msi_free(dir);
7788 return ERROR_OUTOFMEMORY;
7791 lstrcpyW(package->PackagePath, dir);
7792 lstrcatW(package->PackagePath, file);
7793 msi_free(dir);
7795 msi_set_sourcedir_props(package, FALSE);
7798 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7799 if (rc != ERROR_SUCCESS)
7800 return rc;
7802 msi_apply_transforms( package );
7803 msi_apply_patches( package );
7805 patch = msi_dup_property( package->db, szPatch );
7806 remove = msi_dup_property( package->db, szRemove );
7807 reinstall = msi_dup_property( package->db, szReinstall );
7808 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7810 TRACE("setting REINSTALL property to ALL\n");
7811 msi_set_property( package->db, szReinstall, szAll, -1 );
7812 package->full_reinstall = 1;
7815 /* properties may have been added by a transform */
7816 msi_clone_properties( package );
7817 msi_set_original_database_property( package->db, szPackagePath );
7819 msi_parse_command_line( package, szCommandLine, FALSE );
7820 msi_adjust_privilege_properties( package );
7821 msi_set_context( package );
7823 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7825 TRACE("disabling rollback\n");
7826 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7829 if (needs_ui_sequence( package))
7831 package->script->InWhatSequence |= SEQUENCE_UI;
7832 rc = ACTION_ProcessUISequence(package);
7833 ui_exists = ui_sequence_exists(package);
7834 if (rc == ERROR_SUCCESS || !ui_exists)
7836 package->script->InWhatSequence |= SEQUENCE_EXEC;
7837 rc = ACTION_ProcessExecSequence(package, ui_exists);
7840 else
7841 rc = ACTION_ProcessExecSequence(package, FALSE);
7843 package->script->CurrentlyScripting = FALSE;
7845 /* process the ending type action */
7846 if (rc == ERROR_SUCCESS)
7847 ACTION_PerformActionSequence(package, -1);
7848 else if (rc == ERROR_INSTALL_USEREXIT)
7849 ACTION_PerformActionSequence(package, -2);
7850 else if (rc == ERROR_INSTALL_SUSPEND)
7851 ACTION_PerformActionSequence(package, -4);
7852 else /* failed */
7854 ACTION_PerformActionSequence(package, -3);
7855 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7857 package->need_rollback = TRUE;
7861 /* finish up running custom actions */
7862 ACTION_FinishCustomActions(package);
7864 if (package->need_rollback && !reinstall)
7866 WARN("installation failed, running rollback script\n");
7867 execute_script( package, SCRIPT_ROLLBACK );
7869 msi_free( reinstall );
7870 msi_free( remove );
7871 msi_free( patch );
7873 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7874 return ERROR_SUCCESS_REBOOT_REQUIRED;
7876 return rc;