msi: Don't remove products if msidbUpgradeAttributesOnlyDetect is set.
[wine/multimedia.git] / dlls / msi / action.c
blobd48ecddfb688ffd01571781caef50c5fde9d3b0f
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 in_quotes = 1;
226 ignore = 1;
227 len++;
228 break;
229 case '"':
230 state = state_quote;
231 if (in_quotes && p[1] != '\"') count--;
232 else count++;
233 break;
234 default:
235 state = state_token;
236 in_quotes = 1;
237 len++;
238 break;
240 break;
242 case state_token:
243 switch (*p)
245 case '"':
246 state = state_quote;
247 if (in_quotes) count--;
248 else count++;
249 break;
250 case ' ':
251 state = state_whitespace;
252 if (!count) goto done;
253 in_quotes = 1;
254 len++;
255 break;
256 default:
257 if (!count) in_quotes = 0;
258 else in_quotes = 1;
259 len++;
260 break;
262 break;
264 case state_quote:
265 switch (*p)
267 case '"':
268 if (in_quotes && p[1] != '\"') count--;
269 else count++;
270 break;
271 case ' ':
272 state = state_whitespace;
273 if (!count || (count > 1 && !len)) goto done;
274 in_quotes = 1;
275 len++;
276 break;
277 default:
278 state = state_token;
279 if (!count) in_quotes = 0;
280 else in_quotes = 1;
281 len++;
282 break;
284 break;
286 default: break;
288 if (!ignore) *out++ = *p;
291 done:
292 if (!len) *value = 0;
293 else *out = 0;
295 *quotes = count;
296 return p - str;
299 static void remove_quotes( WCHAR *str )
301 WCHAR *p = str;
302 int len = strlenW( str );
304 while ((p = strchrW( p, '"' )))
306 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
307 p++;
311 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
312 BOOL preserve_case )
314 LPCWSTR ptr, ptr2;
315 int num_quotes;
316 DWORD len;
317 WCHAR *prop, *val;
318 UINT r;
320 if (!szCommandLine)
321 return ERROR_SUCCESS;
323 ptr = szCommandLine;
324 while (*ptr)
326 while (*ptr == ' ') ptr++;
327 if (!*ptr) break;
329 ptr2 = strchrW( ptr, '=' );
330 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
332 len = ptr2 - ptr;
333 if (!len) return ERROR_INVALID_COMMAND_LINE;
335 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
336 memcpy( prop, ptr, len * sizeof(WCHAR) );
337 prop[len] = 0;
338 if (!preserve_case) struprW( prop );
340 ptr2++;
341 while (*ptr2 == ' ') ptr2++;
343 num_quotes = 0;
344 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
345 len = parse_prop( ptr2, val, &num_quotes );
346 if (num_quotes % 2)
348 WARN("unbalanced quotes\n");
349 msi_free( val );
350 msi_free( prop );
351 return ERROR_INVALID_COMMAND_LINE;
353 remove_quotes( val );
354 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
356 r = msi_set_property( package->db, prop, val, -1 );
357 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
358 msi_reset_folders( package, TRUE );
360 msi_free( val );
361 msi_free( prop );
363 ptr = ptr2 + len;
366 return ERROR_SUCCESS;
369 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
371 LPCWSTR pc;
372 LPWSTR p, *ret = NULL;
373 UINT count = 0;
375 if (!str)
376 return ret;
378 /* count the number of substrings */
379 for ( pc = str, count = 0; pc; count++ )
381 pc = strchrW( pc, sep );
382 if (pc)
383 pc++;
386 /* allocate space for an array of substring pointers and the substrings */
387 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
388 (lstrlenW(str)+1) * sizeof(WCHAR) );
389 if (!ret)
390 return ret;
392 /* copy the string and set the pointers */
393 p = (LPWSTR) &ret[count+1];
394 lstrcpyW( p, str );
395 for( count = 0; (ret[count] = p); count++ )
397 p = strchrW( p, sep );
398 if (p)
399 *p++ = 0;
402 return ret;
405 static BOOL ui_sequence_exists( MSIPACKAGE *package )
407 static const WCHAR query [] = {
408 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
409 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
410 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
411 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
412 MSIQUERY *view;
413 UINT rc;
415 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
416 if (rc == ERROR_SUCCESS)
418 msiobj_release(&view->hdr);
419 return TRUE;
421 return FALSE;
424 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
426 LPWSTR source, check;
428 if (msi_get_property_int( package->db, szInstalled, 0 ))
430 HKEY hkey;
432 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
433 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
434 RegCloseKey( hkey );
436 else
438 LPWSTR p, db;
439 DWORD len;
441 db = msi_dup_property( package->db, szOriginalDatabase );
442 if (!db)
443 return ERROR_OUTOFMEMORY;
445 p = strrchrW( db, '\\' );
446 if (!p)
448 p = strrchrW( db, '/' );
449 if (!p)
451 msi_free(db);
452 return ERROR_SUCCESS;
456 len = p - db + 2;
457 source = msi_alloc( len * sizeof(WCHAR) );
458 lstrcpynW( source, db, len );
459 msi_free( db );
462 check = msi_dup_property( package->db, szSourceDir );
463 if (!check || replace)
465 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
466 if (r == ERROR_SUCCESS)
467 msi_reset_folders( package, TRUE );
469 msi_free( check );
471 check = msi_dup_property( package->db, szSOURCEDIR );
472 if (!check || replace)
473 msi_set_property( package->db, szSOURCEDIR, source, -1 );
475 msi_free( check );
476 msi_free( source );
478 return ERROR_SUCCESS;
481 static BOOL needs_ui_sequence(MSIPACKAGE *package)
483 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
486 UINT msi_set_context(MSIPACKAGE *package)
488 UINT r = msi_locate_product( package->ProductCode, &package->Context );
489 if (r != ERROR_SUCCESS)
491 int num = msi_get_property_int( package->db, szAllUsers, 0 );
492 if (num == 1 || num == 2)
493 package->Context = MSIINSTALLCONTEXT_MACHINE;
494 else
495 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
497 return ERROR_SUCCESS;
500 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
502 UINT rc;
503 LPCWSTR cond, action;
504 MSIPACKAGE *package = param;
506 action = MSI_RecordGetString(row,1);
507 if (!action)
509 ERR("Error is retrieving action name\n");
510 return ERROR_FUNCTION_FAILED;
513 /* check conditions */
514 cond = MSI_RecordGetString(row,2);
516 /* this is a hack to skip errors in the condition code */
517 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
519 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
520 return ERROR_SUCCESS;
523 if (needs_ui_sequence(package))
524 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
525 else
526 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
528 msi_dialog_check_messages( NULL );
530 if (package->CurrentInstallState != ERROR_SUCCESS)
531 rc = package->CurrentInstallState;
533 if (rc == ERROR_FUNCTION_NOT_CALLED)
534 rc = ERROR_SUCCESS;
536 if (rc != ERROR_SUCCESS)
537 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
539 if (package->need_reboot_now)
541 TRACE("action %s asked for immediate reboot, suspending installation\n",
542 debugstr_w(action));
543 rc = ACTION_ForceReboot( package );
545 return rc;
548 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
550 static const WCHAR query[] = {
551 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
552 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
553 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
554 '`','S','e','q','u','e','n','c','e','`',0};
555 MSIQUERY *view;
556 UINT r;
558 TRACE("%p %s\n", package, debugstr_w(table));
560 r = MSI_OpenQuery( package->db, &view, query, table );
561 if (r == ERROR_SUCCESS)
563 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
564 msiobj_release(&view->hdr);
566 return r;
569 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
571 static const WCHAR query[] = {
572 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
573 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
574 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
575 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
576 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
577 static const WCHAR query_validate[] = {
578 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
579 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
580 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
581 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
582 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
583 MSIQUERY *view;
584 INT seq = 0;
585 UINT rc;
587 if (package->script->ExecuteSequenceRun)
589 TRACE("Execute Sequence already Run\n");
590 return ERROR_SUCCESS;
593 package->script->ExecuteSequenceRun = TRUE;
595 /* get the sequence number */
596 if (UIran)
598 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
599 if (!row) return ERROR_FUNCTION_FAILED;
600 seq = MSI_RecordGetInteger(row,1);
601 msiobj_release(&row->hdr);
603 rc = MSI_OpenQuery(package->db, &view, query, seq);
604 if (rc == ERROR_SUCCESS)
606 TRACE("Running the actions\n");
608 msi_set_property( package->db, szSourceDir, NULL, -1 );
609 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
610 msiobj_release(&view->hdr);
612 return rc;
615 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
617 static const WCHAR query[] = {
618 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
619 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
620 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
621 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
622 MSIQUERY *view;
623 UINT rc;
625 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
626 if (rc == ERROR_SUCCESS)
628 TRACE("Running the actions\n");
629 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
630 msiobj_release(&view->hdr);
632 return rc;
635 /********************************************************
636 * ACTION helper functions and functions that perform the actions
637 *******************************************************/
638 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
639 UINT* rc, UINT script, BOOL force )
641 BOOL ret=FALSE;
642 UINT arc;
644 arc = ACTION_CustomAction(package, action, script, force);
646 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
648 *rc = arc;
649 ret = TRUE;
651 return ret;
654 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
656 MSICOMPONENT *comp;
658 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
660 if (!strcmpW( Component, comp->Component )) return comp;
662 return NULL;
665 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
667 MSIFEATURE *feature;
669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
671 if (!strcmpW( Feature, feature->Feature )) return feature;
673 return NULL;
676 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
678 MSIFILE *file;
680 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
682 if (!strcmpW( key, file->File )) return file;
684 return NULL;
687 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
689 MSIFILEPATCH *patch;
691 /* FIXME: There might be more than one patch */
692 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
694 if (!strcmpW( key, patch->File->File )) return patch;
696 return NULL;
699 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
701 MSIFOLDER *folder;
703 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
705 if (!strcmpW( dir, folder->Directory )) return folder;
707 return NULL;
711 * Recursively create all directories in the path.
712 * shamelessly stolen from setupapi/queue.c
714 BOOL msi_create_full_path( const WCHAR *path )
716 BOOL ret = TRUE;
717 WCHAR *new_path;
718 int len;
720 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
721 strcpyW( new_path, path );
723 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
724 new_path[len - 1] = 0;
726 while (!CreateDirectoryW( new_path, NULL ))
728 WCHAR *slash;
729 DWORD last_error = GetLastError();
730 if (last_error == ERROR_ALREADY_EXISTS) break;
731 if (last_error != ERROR_PATH_NOT_FOUND)
733 ret = FALSE;
734 break;
736 if (!(slash = strrchrW( new_path, '\\' )))
738 ret = FALSE;
739 break;
741 len = slash - new_path;
742 new_path[len] = 0;
743 if (!msi_create_full_path( new_path ))
745 ret = FALSE;
746 break;
748 new_path[len] = '\\';
750 msi_free( new_path );
751 return ret;
754 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
756 MSIRECORD *row;
758 row = MSI_CreateRecord( 4 );
759 MSI_RecordSetInteger( row, 1, a );
760 MSI_RecordSetInteger( row, 2, b );
761 MSI_RecordSetInteger( row, 3, c );
762 MSI_RecordSetInteger( row, 4, d );
763 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
764 msiobj_release( &row->hdr );
766 msi_dialog_check_messages( NULL );
769 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
771 static const WCHAR query[] =
772 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
773 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
774 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
775 WCHAR message[1024];
776 MSIRECORD *row = 0;
777 DWORD size;
779 if (!package->LastAction || strcmpW( package->LastAction, action ))
781 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
783 if (MSI_RecordIsNull( row, 3 ))
785 msiobj_release( &row->hdr );
786 return;
788 /* update the cached action format */
789 msi_free( package->ActionFormat );
790 package->ActionFormat = msi_dup_record_field( row, 3 );
791 msi_free( package->LastAction );
792 package->LastAction = strdupW( action );
793 msiobj_release( &row->hdr );
795 size = 1024;
796 MSI_RecordSetStringW( record, 0, package->ActionFormat );
797 MSI_FormatRecordW( package, record, message, &size );
798 row = MSI_CreateRecord( 1 );
799 MSI_RecordSetStringW( row, 1, message );
800 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
801 msiobj_release( &row->hdr );
804 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
806 if (!comp->Enabled)
808 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
809 return INSTALLSTATE_UNKNOWN;
811 if (package->need_rollback) return comp->Installed;
812 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
814 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
815 return INSTALLSTATE_UNKNOWN;
817 return comp->ActionRequest;
820 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
822 if (package->need_rollback) return feature->Installed;
823 return feature->ActionRequest;
826 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
828 MSIPACKAGE *package = param;
829 LPCWSTR dir, component, full_path;
830 MSIRECORD *uirow;
831 MSIFOLDER *folder;
832 MSICOMPONENT *comp;
834 component = MSI_RecordGetString(row, 2);
835 if (!component)
836 return ERROR_SUCCESS;
838 comp = msi_get_loaded_component(package, component);
839 if (!comp)
840 return ERROR_SUCCESS;
842 comp->Action = msi_get_component_action( package, comp );
843 if (comp->Action != INSTALLSTATE_LOCAL)
845 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
846 return ERROR_SUCCESS;
849 dir = MSI_RecordGetString(row,1);
850 if (!dir)
852 ERR("Unable to get folder id\n");
853 return ERROR_SUCCESS;
856 uirow = MSI_CreateRecord(1);
857 MSI_RecordSetStringW(uirow, 1, dir);
858 msi_ui_actiondata(package, szCreateFolders, uirow);
859 msiobj_release(&uirow->hdr);
861 full_path = msi_get_target_folder( package, dir );
862 if (!full_path)
864 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
865 return ERROR_SUCCESS;
867 TRACE("folder is %s\n", debugstr_w(full_path));
869 folder = msi_get_loaded_folder( package, dir );
870 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
871 folder->State = FOLDER_STATE_CREATED;
872 return ERROR_SUCCESS;
875 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
877 static const WCHAR query[] = {
878 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
879 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
880 MSIQUERY *view;
881 UINT rc;
883 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
884 if (rc != ERROR_SUCCESS)
885 return ERROR_SUCCESS;
887 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
888 msiobj_release(&view->hdr);
889 return rc;
892 static void remove_persistent_folder( MSIFOLDER *folder )
894 FolderList *fl;
896 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
898 remove_persistent_folder( fl->folder );
900 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
902 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
906 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
908 MSIPACKAGE *package = param;
909 LPCWSTR dir, component, full_path;
910 MSIRECORD *uirow;
911 MSIFOLDER *folder;
912 MSICOMPONENT *comp;
914 component = MSI_RecordGetString(row, 2);
915 if (!component)
916 return ERROR_SUCCESS;
918 comp = msi_get_loaded_component(package, component);
919 if (!comp)
920 return ERROR_SUCCESS;
922 comp->Action = msi_get_component_action( package, comp );
923 if (comp->Action != INSTALLSTATE_ABSENT)
925 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
926 return ERROR_SUCCESS;
929 dir = MSI_RecordGetString( row, 1 );
930 if (!dir)
932 ERR("Unable to get folder id\n");
933 return ERROR_SUCCESS;
936 full_path = msi_get_target_folder( package, dir );
937 if (!full_path)
939 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
940 return ERROR_SUCCESS;
942 TRACE("folder is %s\n", debugstr_w(full_path));
944 uirow = MSI_CreateRecord( 1 );
945 MSI_RecordSetStringW( uirow, 1, dir );
946 msi_ui_actiondata( package, szRemoveFolders, uirow );
947 msiobj_release( &uirow->hdr );
949 folder = msi_get_loaded_folder( package, dir );
950 remove_persistent_folder( folder );
951 return ERROR_SUCCESS;
954 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
956 static const WCHAR query[] = {
957 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
958 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
959 MSIQUERY *view;
960 UINT rc;
962 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
963 if (rc != ERROR_SUCCESS)
964 return ERROR_SUCCESS;
966 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
967 msiobj_release( &view->hdr );
968 return rc;
971 static UINT load_component( MSIRECORD *row, LPVOID param )
973 MSIPACKAGE *package = param;
974 MSICOMPONENT *comp;
976 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
977 if (!comp)
978 return ERROR_FUNCTION_FAILED;
980 list_add_tail( &package->components, &comp->entry );
982 /* fill in the data */
983 comp->Component = msi_dup_record_field( row, 1 );
985 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
987 comp->ComponentId = msi_dup_record_field( row, 2 );
988 comp->Directory = msi_dup_record_field( row, 3 );
989 comp->Attributes = MSI_RecordGetInteger(row,4);
990 comp->Condition = msi_dup_record_field( row, 5 );
991 comp->KeyPath = msi_dup_record_field( row, 6 );
993 comp->Installed = INSTALLSTATE_UNKNOWN;
994 comp->Action = INSTALLSTATE_UNKNOWN;
995 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
997 comp->assembly = msi_load_assembly( package, comp );
998 return ERROR_SUCCESS;
1001 UINT msi_load_all_components( MSIPACKAGE *package )
1003 static const WCHAR query[] = {
1004 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1005 '`','C','o','m','p','o','n','e','n','t','`',0};
1006 MSIQUERY *view;
1007 UINT r;
1009 if (!list_empty(&package->components))
1010 return ERROR_SUCCESS;
1012 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1013 if (r != ERROR_SUCCESS)
1014 return r;
1016 if (!msi_init_assembly_caches( package ))
1018 ERR("can't initialize assembly caches\n");
1019 msiobj_release( &view->hdr );
1020 return ERROR_FUNCTION_FAILED;
1023 r = MSI_IterateRecords(view, NULL, load_component, package);
1024 msiobj_release(&view->hdr);
1025 return r;
1028 typedef struct {
1029 MSIPACKAGE *package;
1030 MSIFEATURE *feature;
1031 } _ilfs;
1033 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1035 ComponentList *cl;
1037 cl = msi_alloc( sizeof (*cl) );
1038 if ( !cl )
1039 return ERROR_NOT_ENOUGH_MEMORY;
1040 cl->component = comp;
1041 list_add_tail( &feature->Components, &cl->entry );
1043 return ERROR_SUCCESS;
1046 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1048 FeatureList *fl;
1050 fl = msi_alloc( sizeof(*fl) );
1051 if ( !fl )
1052 return ERROR_NOT_ENOUGH_MEMORY;
1053 fl->feature = child;
1054 list_add_tail( &parent->Children, &fl->entry );
1056 return ERROR_SUCCESS;
1059 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1061 _ilfs* ilfs = param;
1062 LPCWSTR component;
1063 MSICOMPONENT *comp;
1065 component = MSI_RecordGetString(row,1);
1067 /* check to see if the component is already loaded */
1068 comp = msi_get_loaded_component( ilfs->package, component );
1069 if (!comp)
1071 WARN("ignoring unknown component %s\n", debugstr_w(component));
1072 return ERROR_SUCCESS;
1074 add_feature_component( ilfs->feature, comp );
1075 comp->Enabled = TRUE;
1077 return ERROR_SUCCESS;
1080 static UINT load_feature(MSIRECORD * row, LPVOID param)
1082 static const WCHAR query[] = {
1083 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1084 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1085 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1086 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1087 MSIPACKAGE *package = param;
1088 MSIFEATURE *feature;
1089 MSIQUERY *view;
1090 _ilfs ilfs;
1091 UINT rc;
1093 /* fill in the data */
1095 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1096 if (!feature)
1097 return ERROR_NOT_ENOUGH_MEMORY;
1099 list_init( &feature->Children );
1100 list_init( &feature->Components );
1102 feature->Feature = msi_dup_record_field( row, 1 );
1104 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1106 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1107 feature->Title = msi_dup_record_field( row, 3 );
1108 feature->Description = msi_dup_record_field( row, 4 );
1110 if (!MSI_RecordIsNull(row,5))
1111 feature->Display = MSI_RecordGetInteger(row,5);
1113 feature->Level= MSI_RecordGetInteger(row,6);
1114 feature->Directory = msi_dup_record_field( row, 7 );
1115 feature->Attributes = MSI_RecordGetInteger(row,8);
1117 feature->Installed = INSTALLSTATE_UNKNOWN;
1118 feature->Action = INSTALLSTATE_UNKNOWN;
1119 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1121 list_add_tail( &package->features, &feature->entry );
1123 /* load feature components */
1125 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1126 if (rc != ERROR_SUCCESS)
1127 return ERROR_SUCCESS;
1129 ilfs.package = package;
1130 ilfs.feature = feature;
1132 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1133 msiobj_release(&view->hdr);
1134 return rc;
1137 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1139 MSIPACKAGE *package = param;
1140 MSIFEATURE *parent, *child;
1142 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1143 if (!child)
1144 return ERROR_FUNCTION_FAILED;
1146 if (!child->Feature_Parent)
1147 return ERROR_SUCCESS;
1149 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1150 if (!parent)
1151 return ERROR_FUNCTION_FAILED;
1153 add_feature_child( parent, child );
1154 return ERROR_SUCCESS;
1157 UINT msi_load_all_features( MSIPACKAGE *package )
1159 static const WCHAR query[] = {
1160 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1161 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1162 '`','D','i','s','p','l','a','y','`',0};
1163 MSIQUERY *view;
1164 UINT r;
1166 if (!list_empty(&package->features))
1167 return ERROR_SUCCESS;
1169 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1170 if (r != ERROR_SUCCESS)
1171 return r;
1173 r = MSI_IterateRecords( view, NULL, load_feature, package );
1174 if (r != ERROR_SUCCESS)
1176 msiobj_release( &view->hdr );
1177 return r;
1179 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1180 msiobj_release( &view->hdr );
1181 return r;
1184 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1186 if (!p)
1187 return p;
1188 p = strchrW(p, ch);
1189 if (!p)
1190 return p;
1191 *p = 0;
1192 return p+1;
1195 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1197 static const WCHAR query[] = {
1198 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1199 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1200 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1201 MSIQUERY *view = NULL;
1202 MSIRECORD *row = NULL;
1203 UINT r;
1205 TRACE("%s\n", debugstr_w(file->File));
1207 r = MSI_OpenQuery(package->db, &view, query, file->File);
1208 if (r != ERROR_SUCCESS)
1209 goto done;
1211 r = MSI_ViewExecute(view, NULL);
1212 if (r != ERROR_SUCCESS)
1213 goto done;
1215 r = MSI_ViewFetch(view, &row);
1216 if (r != ERROR_SUCCESS)
1217 goto done;
1219 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1220 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1221 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1222 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1223 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1225 done:
1226 if (view) msiobj_release(&view->hdr);
1227 if (row) msiobj_release(&row->hdr);
1228 return r;
1231 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1233 MSIRECORD *row;
1234 static const WCHAR query[] = {
1235 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1236 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1237 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1239 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1240 if (!row)
1242 WARN("query failed\n");
1243 return ERROR_FUNCTION_FAILED;
1246 file->disk_id = MSI_RecordGetInteger( row, 1 );
1247 msiobj_release( &row->hdr );
1248 return ERROR_SUCCESS;
1251 static UINT load_file(MSIRECORD *row, LPVOID param)
1253 MSIPACKAGE* package = param;
1254 LPCWSTR component;
1255 MSIFILE *file;
1257 /* fill in the data */
1259 file = msi_alloc_zero( sizeof (MSIFILE) );
1260 if (!file)
1261 return ERROR_NOT_ENOUGH_MEMORY;
1263 file->File = msi_dup_record_field( row, 1 );
1265 component = MSI_RecordGetString( row, 2 );
1266 file->Component = msi_get_loaded_component( package, component );
1268 if (!file->Component)
1270 WARN("Component not found: %s\n", debugstr_w(component));
1271 msi_free(file->File);
1272 msi_free(file);
1273 return ERROR_SUCCESS;
1276 file->FileName = msi_dup_record_field( row, 3 );
1277 msi_reduce_to_long_filename( file->FileName );
1279 file->ShortName = msi_dup_record_field( row, 3 );
1280 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1282 file->FileSize = MSI_RecordGetInteger( row, 4 );
1283 file->Version = msi_dup_record_field( row, 5 );
1284 file->Language = msi_dup_record_field( row, 6 );
1285 file->Attributes = MSI_RecordGetInteger( row, 7 );
1286 file->Sequence = MSI_RecordGetInteger( row, 8 );
1288 file->state = msifs_invalid;
1290 /* if the compressed bits are not set in the file attributes,
1291 * then read the information from the package word count property
1293 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1295 file->IsCompressed = FALSE;
1297 else if (file->Attributes &
1298 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1300 file->IsCompressed = TRUE;
1302 else if (file->Attributes & msidbFileAttributesNoncompressed)
1304 file->IsCompressed = FALSE;
1306 else
1308 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1311 load_file_hash(package, file);
1312 load_file_disk_id(package, file);
1314 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1316 list_add_tail( &package->files, &file->entry );
1318 return ERROR_SUCCESS;
1321 static UINT load_all_files(MSIPACKAGE *package)
1323 static const WCHAR query[] = {
1324 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1325 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1326 '`','S','e','q','u','e','n','c','e','`', 0};
1327 MSIQUERY *view;
1328 UINT rc;
1330 if (!list_empty(&package->files))
1331 return ERROR_SUCCESS;
1333 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1334 if (rc != ERROR_SUCCESS)
1335 return ERROR_SUCCESS;
1337 rc = MSI_IterateRecords(view, NULL, load_file, package);
1338 msiobj_release(&view->hdr);
1339 return rc;
1342 static UINT load_media( MSIRECORD *row, LPVOID param )
1344 MSIPACKAGE *package = param;
1345 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1346 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1348 /* FIXME: load external cabinets and directory sources too */
1349 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1350 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1351 return ERROR_SUCCESS;
1354 static UINT load_all_media( MSIPACKAGE *package )
1356 static const WCHAR query[] = {
1357 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1358 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1359 '`','D','i','s','k','I','d','`',0};
1360 MSIQUERY *view;
1361 UINT r;
1363 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1364 if (r != ERROR_SUCCESS)
1365 return ERROR_SUCCESS;
1367 r = MSI_IterateRecords( view, NULL, load_media, package );
1368 msiobj_release( &view->hdr );
1369 return r;
1372 static UINT load_patch(MSIRECORD *row, LPVOID param)
1374 MSIPACKAGE *package = param;
1375 MSIFILEPATCH *patch;
1376 LPWSTR file_key;
1378 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1379 if (!patch)
1380 return ERROR_NOT_ENOUGH_MEMORY;
1382 file_key = msi_dup_record_field( row, 1 );
1383 patch->File = msi_get_loaded_file( package, file_key );
1384 msi_free(file_key);
1386 if( !patch->File )
1388 ERR("Failed to find target for patch in File table\n");
1389 msi_free(patch);
1390 return ERROR_FUNCTION_FAILED;
1393 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1395 /* FIXME: The database should be properly transformed */
1396 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1398 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1399 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1400 patch->IsApplied = FALSE;
1402 /* FIXME:
1403 * Header field - for patch validation.
1404 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1407 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1409 list_add_tail( &package->filepatches, &patch->entry );
1411 return ERROR_SUCCESS;
1414 static UINT load_all_patches(MSIPACKAGE *package)
1416 static const WCHAR query[] = {
1417 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1418 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1419 '`','S','e','q','u','e','n','c','e','`',0};
1420 MSIQUERY *view;
1421 UINT rc;
1423 if (!list_empty(&package->filepatches))
1424 return ERROR_SUCCESS;
1426 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1427 if (rc != ERROR_SUCCESS)
1428 return ERROR_SUCCESS;
1430 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1431 msiobj_release(&view->hdr);
1432 return rc;
1435 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1437 static const WCHAR query[] = {
1438 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1439 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1440 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1441 MSIQUERY *view;
1443 folder->persistent = FALSE;
1444 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1446 if (!MSI_ViewExecute( view, NULL ))
1448 MSIRECORD *rec;
1449 if (!MSI_ViewFetch( view, &rec ))
1451 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1452 folder->persistent = TRUE;
1453 msiobj_release( &rec->hdr );
1456 msiobj_release( &view->hdr );
1458 return ERROR_SUCCESS;
1461 static UINT load_folder( MSIRECORD *row, LPVOID param )
1463 MSIPACKAGE *package = param;
1464 static WCHAR szEmpty[] = { 0 };
1465 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1466 MSIFOLDER *folder;
1468 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1469 list_init( &folder->children );
1470 folder->Directory = msi_dup_record_field( row, 1 );
1471 folder->Parent = msi_dup_record_field( row, 2 );
1472 p = msi_dup_record_field(row, 3);
1474 TRACE("%s\n", debugstr_w(folder->Directory));
1476 /* split src and target dir */
1477 tgt_short = p;
1478 src_short = folder_split_path( p, ':' );
1480 /* split the long and short paths */
1481 tgt_long = folder_split_path( tgt_short, '|' );
1482 src_long = folder_split_path( src_short, '|' );
1484 /* check for no-op dirs */
1485 if (tgt_short && !strcmpW( szDot, tgt_short ))
1486 tgt_short = szEmpty;
1487 if (src_short && !strcmpW( szDot, src_short ))
1488 src_short = szEmpty;
1490 if (!tgt_long)
1491 tgt_long = tgt_short;
1493 if (!src_short) {
1494 src_short = tgt_short;
1495 src_long = tgt_long;
1498 if (!src_long)
1499 src_long = src_short;
1501 /* FIXME: use the target short path too */
1502 folder->TargetDefault = strdupW(tgt_long);
1503 folder->SourceShortPath = strdupW(src_short);
1504 folder->SourceLongPath = strdupW(src_long);
1505 msi_free(p);
1507 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1508 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1509 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1511 load_folder_persistence( package, folder );
1513 list_add_tail( &package->folders, &folder->entry );
1514 return ERROR_SUCCESS;
1517 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1519 FolderList *fl;
1521 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1522 fl->folder = child;
1523 list_add_tail( &parent->children, &fl->entry );
1524 return ERROR_SUCCESS;
1527 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1529 MSIPACKAGE *package = param;
1530 MSIFOLDER *parent, *child;
1532 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1533 return ERROR_FUNCTION_FAILED;
1535 if (!child->Parent) return ERROR_SUCCESS;
1537 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1538 return ERROR_FUNCTION_FAILED;
1540 return add_folder_child( parent, child );
1543 static UINT load_all_folders( MSIPACKAGE *package )
1545 static const WCHAR query[] = {
1546 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1547 '`','D','i','r','e','c','t','o','r','y','`',0};
1548 MSIQUERY *view;
1549 UINT r;
1551 if (!list_empty(&package->folders))
1552 return ERROR_SUCCESS;
1554 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1555 if (r != ERROR_SUCCESS)
1556 return r;
1558 r = MSI_IterateRecords( view, NULL, load_folder, package );
1559 if (r != ERROR_SUCCESS)
1561 msiobj_release( &view->hdr );
1562 return r;
1564 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1565 msiobj_release( &view->hdr );
1566 return r;
1569 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1571 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1572 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1574 load_all_folders( package );
1575 msi_load_all_components( package );
1576 msi_load_all_features( package );
1577 load_all_files( package );
1578 load_all_patches( package );
1579 load_all_media( package );
1581 return ERROR_SUCCESS;
1584 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1586 const WCHAR *action = package->script->Actions[script][index];
1587 ui_actionstart( package, action );
1588 TRACE("executing %s\n", debugstr_w(action));
1589 return ACTION_PerformAction( package, action, script );
1592 static UINT execute_script( MSIPACKAGE *package, UINT script )
1594 UINT i, rc = ERROR_SUCCESS;
1596 TRACE("executing script %u\n", script);
1598 if (!package->script)
1600 ERR("no script!\n");
1601 return ERROR_FUNCTION_FAILED;
1603 if (script == SCRIPT_ROLLBACK)
1605 for (i = package->script->ActionCount[script]; i > 0; i--)
1607 rc = execute_script_action( package, script, i - 1 );
1608 if (rc != ERROR_SUCCESS) break;
1611 else
1613 for (i = 0; i < package->script->ActionCount[script]; i++)
1615 rc = execute_script_action( package, script, i );
1616 if (rc != ERROR_SUCCESS) break;
1619 msi_free_action_script(package, script);
1620 return rc;
1623 static UINT ACTION_FileCost(MSIPACKAGE *package)
1625 return ERROR_SUCCESS;
1628 static void get_client_counts( MSIPACKAGE *package )
1630 MSICOMPONENT *comp;
1631 HKEY hkey;
1633 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1635 if (!comp->ComponentId) continue;
1637 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1638 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1640 comp->num_clients = 0;
1641 continue;
1643 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1644 NULL, NULL, NULL, NULL );
1645 RegCloseKey( hkey );
1649 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1651 MSICOMPONENT *comp;
1652 UINT r;
1654 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1656 if (!comp->ComponentId) continue;
1658 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1659 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1660 &comp->Installed );
1661 if (r == ERROR_SUCCESS) continue;
1663 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1664 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1665 &comp->Installed );
1666 if (r == ERROR_SUCCESS) continue;
1668 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1669 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1670 &comp->Installed );
1671 if (r == ERROR_SUCCESS) continue;
1673 comp->Installed = INSTALLSTATE_ABSENT;
1677 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1679 MSIFEATURE *feature;
1681 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1683 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1685 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1686 feature->Installed = INSTALLSTATE_ABSENT;
1687 else
1688 feature->Installed = state;
1692 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1694 return (feature->Level > 0 && feature->Level <= level);
1697 static BOOL process_state_property(MSIPACKAGE* package, int level,
1698 LPCWSTR property, INSTALLSTATE state)
1700 LPWSTR override;
1701 MSIFEATURE *feature;
1703 override = msi_dup_property( package->db, property );
1704 if (!override)
1705 return FALSE;
1707 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1709 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1710 continue;
1712 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1714 if (!strcmpiW( override, szAll ))
1716 if (feature->Installed != state)
1718 feature->Action = state;
1719 feature->ActionRequest = state;
1722 else
1724 LPWSTR ptr = override;
1725 LPWSTR ptr2 = strchrW(override,',');
1727 while (ptr)
1729 int len = ptr2 - ptr;
1731 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1732 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1734 if (feature->Installed != state)
1736 feature->Action = state;
1737 feature->ActionRequest = state;
1739 break;
1741 if (ptr2)
1743 ptr=ptr2+1;
1744 ptr2 = strchrW(ptr,',');
1746 else
1747 break;
1751 msi_free(override);
1752 return TRUE;
1755 static BOOL process_overrides( MSIPACKAGE *package, int level )
1757 static const WCHAR szAddLocal[] =
1758 {'A','D','D','L','O','C','A','L',0};
1759 static const WCHAR szAddSource[] =
1760 {'A','D','D','S','O','U','R','C','E',0};
1761 static const WCHAR szAdvertise[] =
1762 {'A','D','V','E','R','T','I','S','E',0};
1763 BOOL ret = FALSE;
1765 /* all these activation/deactivation things happen in order and things
1766 * later on the list override things earlier on the list.
1768 * 0 INSTALLLEVEL processing
1769 * 1 ADDLOCAL
1770 * 2 REMOVE
1771 * 3 ADDSOURCE
1772 * 4 ADDDEFAULT
1773 * 5 REINSTALL
1774 * 6 ADVERTISE
1775 * 7 COMPADDLOCAL
1776 * 8 COMPADDSOURCE
1777 * 9 FILEADDLOCAL
1778 * 10 FILEADDSOURCE
1779 * 11 FILEADDDEFAULT
1781 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1782 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1783 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1784 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1785 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1787 if (ret && !package->full_reinstall)
1788 msi_set_property( package->db, szPreselected, szOne, -1 );
1790 return ret;
1793 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1795 int level;
1796 MSICOMPONENT* component;
1797 MSIFEATURE *feature;
1799 TRACE("Checking Install Level\n");
1801 level = msi_get_property_int(package->db, szInstallLevel, 1);
1803 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1805 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1807 if (!is_feature_selected( feature, level )) continue;
1809 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1811 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1813 feature->Action = INSTALLSTATE_SOURCE;
1814 feature->ActionRequest = INSTALLSTATE_SOURCE;
1816 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1818 feature->Action = INSTALLSTATE_ADVERTISED;
1819 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1821 else
1823 feature->Action = INSTALLSTATE_LOCAL;
1824 feature->ActionRequest = INSTALLSTATE_LOCAL;
1828 /* disable child features of unselected parent or follow parent */
1829 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1831 FeatureList *fl;
1833 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1835 if (!is_feature_selected( feature, level ))
1837 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1838 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1840 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1842 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1843 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1844 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1845 fl->feature->Action = feature->Action;
1846 fl->feature->ActionRequest = feature->ActionRequest;
1851 else /* preselected */
1853 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1855 if (!is_feature_selected( feature, level )) continue;
1857 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1859 if (feature->Installed == INSTALLSTATE_ABSENT)
1861 feature->Action = INSTALLSTATE_UNKNOWN;
1862 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1864 else
1866 feature->Action = feature->Installed;
1867 feature->ActionRequest = feature->Installed;
1871 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1873 FeatureList *fl;
1875 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1877 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1878 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1880 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1881 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1882 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1883 fl->feature->Action = feature->Action;
1884 fl->feature->ActionRequest = feature->ActionRequest;
1890 /* now we want to set component state based based on feature state */
1891 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1893 ComponentList *cl;
1895 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1896 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1897 feature->ActionRequest, feature->Action);
1899 if (!is_feature_selected( feature, level )) continue;
1901 /* features with components that have compressed files are made local */
1902 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1904 if (cl->component->ForceLocalState &&
1905 feature->ActionRequest == INSTALLSTATE_SOURCE)
1907 feature->Action = INSTALLSTATE_LOCAL;
1908 feature->ActionRequest = INSTALLSTATE_LOCAL;
1909 break;
1913 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1915 component = cl->component;
1917 switch (feature->ActionRequest)
1919 case INSTALLSTATE_ABSENT:
1920 component->anyAbsent = 1;
1921 break;
1922 case INSTALLSTATE_ADVERTISED:
1923 component->hasAdvertiseFeature = 1;
1924 break;
1925 case INSTALLSTATE_SOURCE:
1926 component->hasSourceFeature = 1;
1927 break;
1928 case INSTALLSTATE_LOCAL:
1929 component->hasLocalFeature = 1;
1930 break;
1931 case INSTALLSTATE_DEFAULT:
1932 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1933 component->hasAdvertiseFeature = 1;
1934 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1935 component->hasSourceFeature = 1;
1936 else
1937 component->hasLocalFeature = 1;
1938 break;
1939 default:
1940 break;
1945 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1947 /* check if it's local or source */
1948 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1949 (component->hasLocalFeature || component->hasSourceFeature))
1951 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1952 !component->ForceLocalState)
1954 component->Action = INSTALLSTATE_SOURCE;
1955 component->ActionRequest = INSTALLSTATE_SOURCE;
1957 else
1959 component->Action = INSTALLSTATE_LOCAL;
1960 component->ActionRequest = INSTALLSTATE_LOCAL;
1962 continue;
1965 /* if any feature is local, the component must be local too */
1966 if (component->hasLocalFeature)
1968 component->Action = INSTALLSTATE_LOCAL;
1969 component->ActionRequest = INSTALLSTATE_LOCAL;
1970 continue;
1972 if (component->hasSourceFeature)
1974 component->Action = INSTALLSTATE_SOURCE;
1975 component->ActionRequest = INSTALLSTATE_SOURCE;
1976 continue;
1978 if (component->hasAdvertiseFeature)
1980 component->Action = INSTALLSTATE_ADVERTISED;
1981 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1982 continue;
1984 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1985 if (component->anyAbsent && component->ComponentId)
1987 component->Action = INSTALLSTATE_ABSENT;
1988 component->ActionRequest = INSTALLSTATE_ABSENT;
1992 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1994 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1996 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1997 component->Action = INSTALLSTATE_LOCAL;
1998 component->ActionRequest = INSTALLSTATE_LOCAL;
2001 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
2002 component->Installed == INSTALLSTATE_SOURCE &&
2003 component->hasSourceFeature)
2005 component->Action = INSTALLSTATE_UNKNOWN;
2006 component->ActionRequest = INSTALLSTATE_UNKNOWN;
2009 TRACE("component %s (installed %d request %d action %d)\n",
2010 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2012 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
2013 component->num_clients++;
2014 else if (component->Action == INSTALLSTATE_ABSENT)
2015 component->num_clients--;
2018 return ERROR_SUCCESS;
2021 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2023 MSIPACKAGE *package = param;
2024 LPCWSTR name;
2025 MSIFEATURE *feature;
2027 name = MSI_RecordGetString( row, 1 );
2029 feature = msi_get_loaded_feature( package, name );
2030 if (!feature)
2031 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2032 else
2034 LPCWSTR Condition;
2035 Condition = MSI_RecordGetString(row,3);
2037 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2039 int level = MSI_RecordGetInteger(row,2);
2040 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2041 feature->Level = level;
2044 return ERROR_SUCCESS;
2047 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2049 static const WCHAR name[] = {'\\',0};
2050 VS_FIXEDFILEINFO *ptr, *ret;
2051 LPVOID version;
2052 DWORD versize, handle;
2053 UINT sz;
2055 versize = GetFileVersionInfoSizeW( filename, &handle );
2056 if (!versize)
2057 return NULL;
2059 version = msi_alloc( versize );
2060 if (!version)
2061 return NULL;
2063 GetFileVersionInfoW( filename, 0, versize, version );
2065 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2067 msi_free( version );
2068 return NULL;
2071 ret = msi_alloc( sz );
2072 memcpy( ret, ptr, sz );
2074 msi_free( version );
2075 return ret;
2078 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2080 DWORD ms, ls;
2082 msi_parse_version_string( version, &ms, &ls );
2084 if (fi->dwFileVersionMS > ms) return 1;
2085 else if (fi->dwFileVersionMS < ms) return -1;
2086 else if (fi->dwFileVersionLS > ls) return 1;
2087 else if (fi->dwFileVersionLS < ls) return -1;
2088 return 0;
2091 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2093 DWORD ms1, ms2;
2095 msi_parse_version_string( ver1, &ms1, NULL );
2096 msi_parse_version_string( ver2, &ms2, NULL );
2098 if (ms1 > ms2) return 1;
2099 else if (ms1 < ms2) return -1;
2100 return 0;
2103 DWORD msi_get_disk_file_size( LPCWSTR filename )
2105 HANDLE file;
2106 DWORD size;
2108 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2109 if (file == INVALID_HANDLE_VALUE)
2110 return INVALID_FILE_SIZE;
2112 size = GetFileSize( file, NULL );
2113 TRACE("size is %u\n", size);
2114 CloseHandle( file );
2115 return size;
2118 BOOL msi_file_hash_matches( MSIFILE *file )
2120 UINT r;
2121 MSIFILEHASHINFO hash;
2123 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2124 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2125 if (r != ERROR_SUCCESS)
2126 return FALSE;
2128 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2131 static WCHAR *get_temp_dir( void )
2133 static UINT id;
2134 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2136 GetTempPathW( MAX_PATH, tmp );
2137 for (;;)
2139 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2140 if (CreateDirectoryW( dir, NULL )) break;
2142 return strdupW( dir );
2146 * msi_build_directory_name()
2148 * This function is to save messing round with directory names
2149 * It handles adding backslashes between path segments,
2150 * and can add \ at the end of the directory name if told to.
2152 * It takes a variable number of arguments.
2153 * It always allocates a new string for the result, so make sure
2154 * to free the return value when finished with it.
2156 * The first arg is the number of path segments that follow.
2157 * The arguments following count are a list of path segments.
2158 * A path segment may be NULL.
2160 * Path segments will be added with a \ separating them.
2161 * A \ will not be added after the last segment, however if the
2162 * last segment is NULL, then the last character will be a \
2164 WCHAR *msi_build_directory_name( DWORD count, ... )
2166 DWORD sz = 1, i;
2167 WCHAR *dir;
2168 va_list va;
2170 va_start( va, count );
2171 for (i = 0; i < count; i++)
2173 const WCHAR *str = va_arg( va, const WCHAR * );
2174 if (str) sz += strlenW( str ) + 1;
2176 va_end( va );
2178 dir = msi_alloc( sz * sizeof(WCHAR) );
2179 dir[0] = 0;
2181 va_start( va, count );
2182 for (i = 0; i < count; i++)
2184 const WCHAR *str = va_arg( va, const WCHAR * );
2185 if (!str) continue;
2186 strcatW( dir, str );
2187 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2189 va_end( va );
2190 return dir;
2193 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2195 MSIASSEMBLY *assembly = file->Component->assembly;
2197 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2199 msi_free( file->TargetPath );
2200 if (assembly && !assembly->application)
2202 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2203 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2204 msi_track_tempfile( package, file->TargetPath );
2206 else
2208 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2209 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2212 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2215 static UINT calculate_file_cost( MSIPACKAGE *package )
2217 VS_FIXEDFILEINFO *file_version;
2218 WCHAR *font_version;
2219 MSIFILE *file;
2221 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2223 MSICOMPONENT *comp = file->Component;
2224 DWORD file_size;
2226 if (!comp->Enabled) continue;
2228 if (file->IsCompressed)
2229 comp->ForceLocalState = TRUE;
2231 set_target_path( package, file );
2233 if ((comp->assembly && !comp->assembly->installed) ||
2234 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2236 comp->Cost += file->FileSize;
2237 continue;
2239 file_size = msi_get_disk_file_size( file->TargetPath );
2241 if (file->Version)
2243 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2245 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2247 comp->Cost += file->FileSize - file_size;
2249 msi_free( file_version );
2250 continue;
2252 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2254 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2256 comp->Cost += file->FileSize - file_size;
2258 msi_free( font_version );
2259 continue;
2262 if (file_size != file->FileSize)
2264 comp->Cost += file->FileSize - file_size;
2267 return ERROR_SUCCESS;
2270 WCHAR *msi_normalize_path( const WCHAR *in )
2272 const WCHAR *p = in;
2273 WCHAR *q, *ret;
2274 int n, len = strlenW( in ) + 2;
2276 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2278 len = 0;
2279 while (1)
2281 /* copy until the end of the string or a space */
2282 while (*p != ' ' && (*q = *p))
2284 p++, len++;
2285 /* reduce many backslashes to one */
2286 if (*p != '\\' || *q != '\\')
2287 q++;
2290 /* quit at the end of the string */
2291 if (!*p)
2292 break;
2294 /* count the number of spaces */
2295 n = 0;
2296 while (p[n] == ' ')
2297 n++;
2299 /* if it's leading or trailing space, skip it */
2300 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2301 p += n;
2302 else /* copy n spaces */
2303 while (n && (*q++ = *p++)) n--;
2305 while (q - ret > 0 && q[-1] == ' ') q--;
2306 if (q - ret > 0 && q[-1] != '\\')
2308 q[0] = '\\';
2309 q[1] = 0;
2311 return ret;
2314 static WCHAR *get_install_location( MSIPACKAGE *package )
2316 HKEY hkey;
2317 WCHAR *path;
2319 if (!package->ProductCode) return NULL;
2320 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE ))
2321 return NULL;
2322 path = msi_reg_get_val_str( hkey, szInstallLocation );
2323 RegCloseKey( hkey );
2324 return path;
2327 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2329 FolderList *fl;
2330 MSIFOLDER *folder, *parent, *child;
2331 WCHAR *path, *normalized_path;
2333 TRACE("resolving %s\n", debugstr_w(name));
2335 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2337 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2339 if (!(path = get_install_location( package )) &&
2340 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2342 path = msi_dup_property( package->db, szRootDrive );
2345 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2347 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2349 parent = msi_get_loaded_folder( package, folder->Parent );
2350 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2352 else
2353 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2355 normalized_path = msi_normalize_path( path );
2356 msi_free( path );
2357 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2359 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2360 msi_free( normalized_path );
2361 return;
2363 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2364 msi_free( folder->ResolvedTarget );
2365 folder->ResolvedTarget = normalized_path;
2367 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2369 child = fl->folder;
2370 msi_resolve_target_folder( package, child->Directory, load_prop );
2372 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2375 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2377 static const WCHAR query[] = {
2378 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2379 '`','C','o','n','d','i','t','i','o','n','`',0};
2380 static const WCHAR szOutOfDiskSpace[] = {
2381 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2382 MSICOMPONENT *comp;
2383 MSIQUERY *view;
2384 LPWSTR level;
2385 UINT rc;
2387 TRACE("Building directory properties\n");
2388 msi_resolve_target_folder( package, szTargetDir, TRUE );
2390 TRACE("Evaluating component conditions\n");
2391 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2393 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2395 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2396 comp->Enabled = FALSE;
2398 else
2399 comp->Enabled = TRUE;
2401 get_client_counts( package );
2403 /* read components states from the registry */
2404 ACTION_GetComponentInstallStates(package);
2405 ACTION_GetFeatureInstallStates(package);
2407 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2409 TRACE("Evaluating feature conditions\n");
2411 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2412 if (rc == ERROR_SUCCESS)
2414 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2415 msiobj_release( &view->hdr );
2416 if (rc != ERROR_SUCCESS)
2417 return rc;
2421 TRACE("Calculating file cost\n");
2422 calculate_file_cost( package );
2424 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2425 /* set default run level if not set */
2426 level = msi_dup_property( package->db, szInstallLevel );
2427 if (!level)
2428 msi_set_property( package->db, szInstallLevel, szOne, -1 );
2429 msi_free(level);
2431 /* FIXME: check volume disk space */
2432 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2434 return MSI_SetFeatureStates(package);
2437 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size )
2439 BYTE *data = NULL;
2441 if (!value)
2443 *size = sizeof(WCHAR);
2444 *type = REG_SZ;
2445 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2446 return data;
2448 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2450 if (value[1]=='x')
2452 LPWSTR ptr;
2453 CHAR byte[5];
2454 LPWSTR deformated = NULL;
2455 int count;
2457 deformat_string(package, &value[2], &deformated);
2459 /* binary value type */
2460 ptr = deformated;
2461 *type = REG_BINARY;
2462 if (strlenW(ptr)%2)
2463 *size = (strlenW(ptr)/2)+1;
2464 else
2465 *size = strlenW(ptr)/2;
2467 data = msi_alloc(*size);
2469 byte[0] = '0';
2470 byte[1] = 'x';
2471 byte[4] = 0;
2472 count = 0;
2473 /* if uneven pad with a zero in front */
2474 if (strlenW(ptr)%2)
2476 byte[2]= '0';
2477 byte[3]= *ptr;
2478 ptr++;
2479 data[count] = (BYTE)strtol(byte,NULL,0);
2480 count ++;
2481 TRACE("Uneven byte count\n");
2483 while (*ptr)
2485 byte[2]= *ptr;
2486 ptr++;
2487 byte[3]= *ptr;
2488 ptr++;
2489 data[count] = (BYTE)strtol(byte,NULL,0);
2490 count ++;
2492 msi_free(deformated);
2494 TRACE("Data %i bytes(%i)\n",*size,count);
2496 else
2498 LPWSTR deformated;
2499 LPWSTR p;
2500 DWORD d = 0;
2501 deformat_string(package, &value[1], &deformated);
2503 *type=REG_DWORD;
2504 *size = sizeof(DWORD);
2505 data = msi_alloc(*size);
2506 p = deformated;
2507 if (*p == '-')
2508 p++;
2509 while (*p)
2511 if ( (*p < '0') || (*p > '9') )
2512 break;
2513 d *= 10;
2514 d += (*p - '0');
2515 p++;
2517 if (deformated[0] == '-')
2518 d = -d;
2519 *(LPDWORD)data = d;
2520 TRACE("DWORD %i\n",*(LPDWORD)data);
2522 msi_free(deformated);
2525 else
2527 const WCHAR *ptr = value;
2528 DWORD len;
2530 *type = REG_SZ;
2531 if (value[0] == '#')
2533 ptr++;
2534 if (value[1] == '%')
2536 ptr++;
2537 *type = REG_EXPAND_SZ;
2540 len = deformat_string( package, ptr, (WCHAR **)&data );
2541 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2542 *size = (len + 1) * sizeof(WCHAR);
2544 return data;
2547 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2549 const WCHAR *ret;
2551 switch (root)
2553 case -1:
2554 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2556 *root_key = HKEY_LOCAL_MACHINE;
2557 ret = szHLM;
2559 else
2561 *root_key = HKEY_CURRENT_USER;
2562 ret = szHCU;
2564 break;
2565 case 0:
2566 *root_key = HKEY_CLASSES_ROOT;
2567 ret = szHCR;
2568 break;
2569 case 1:
2570 *root_key = HKEY_CURRENT_USER;
2571 ret = szHCU;
2572 break;
2573 case 2:
2574 *root_key = HKEY_LOCAL_MACHINE;
2575 ret = szHLM;
2576 break;
2577 case 3:
2578 *root_key = HKEY_USERS;
2579 ret = szHU;
2580 break;
2581 default:
2582 ERR("Unknown root %i\n", root);
2583 return NULL;
2586 return ret;
2589 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2591 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2592 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2594 if ((is_64bit || is_wow64) &&
2595 !(comp->Attributes & msidbComponentAttributes64bit) &&
2596 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2598 UINT size;
2599 WCHAR *path_32node;
2601 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2602 if (!(path_32node = msi_alloc( size ))) return NULL;
2604 memcpy( path_32node, path, len * sizeof(WCHAR) );
2605 strcpyW( path_32node + len, szWow6432Node );
2606 strcatW( path_32node, szBackSlash );
2607 strcatW( path_32node, path + len );
2608 return path_32node;
2610 return strdupW( path );
2613 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2615 REGSAM access = KEY_ALL_ACCESS;
2616 WCHAR *subkey, *p, *q;
2617 HKEY hkey, ret = NULL;
2618 LONG res;
2620 if (is_wow64) access |= KEY_WOW64_64KEY;
2622 if (!(subkey = strdupW( path ))) return NULL;
2623 p = subkey;
2624 if ((q = strchrW( p, '\\' ))) *q = 0;
2625 if (create)
2626 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2627 else
2628 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2629 if (res)
2631 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2632 msi_free( subkey );
2633 return NULL;
2635 if (q && q[1])
2637 ret = open_key( hkey, q + 1, create );
2638 RegCloseKey( hkey );
2640 else ret = hkey;
2641 msi_free( subkey );
2642 return ret;
2645 static BOOL is_special_entry( const WCHAR *name )
2647 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2650 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2652 const WCHAR *p = str;
2653 WCHAR **ret;
2654 int i = 0;
2656 *count = 0;
2657 if (!str) return NULL;
2658 while ((p - str) < len)
2660 p += strlenW( p ) + 1;
2661 (*count)++;
2663 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2664 p = str;
2665 while ((p - str) < len)
2667 if (!(ret[i] = strdupW( p )))
2669 for (; i >= 0; i--) msi_free( ret[i] );
2670 msi_free( ret );
2671 return NULL;
2673 p += strlenW( p ) + 1;
2674 i++;
2676 return ret;
2679 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2680 WCHAR **right, DWORD right_count, DWORD *size )
2682 WCHAR *ret, *p;
2683 unsigned int i;
2685 *size = sizeof(WCHAR);
2686 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2687 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2689 if (!(ret = p = msi_alloc( *size ))) return NULL;
2691 for (i = 0; i < left_count; i++)
2693 strcpyW( p, left[i] );
2694 p += strlenW( p ) + 1;
2696 for (i = 0; i < right_count; i++)
2698 strcpyW( p, right[i] );
2699 p += strlenW( p ) + 1;
2701 *p = 0;
2702 return ret;
2705 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2706 WCHAR **new, DWORD new_count )
2708 DWORD ret = old_count;
2709 unsigned int i, j, k;
2711 for (i = 0; i < new_count; i++)
2713 for (j = 0; j < old_count; j++)
2715 if (old[j] && !strcmpW( new[i], old[j] ))
2717 msi_free( old[j] );
2718 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2719 old[k] = NULL;
2720 ret--;
2724 return ret;
2727 enum join_op
2729 JOIN_OP_APPEND,
2730 JOIN_OP_PREPEND,
2731 JOIN_OP_REPLACE
2734 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2735 WCHAR **new, DWORD new_count, DWORD *size )
2737 switch (op)
2739 case JOIN_OP_APPEND:
2740 old_count = remove_duplicate_values( old, old_count, new, new_count );
2741 return flatten_multi_string_values( old, old_count, new, new_count, size );
2743 case JOIN_OP_PREPEND:
2744 old_count = remove_duplicate_values( old, old_count, new, new_count );
2745 return flatten_multi_string_values( new, new_count, old, old_count, size );
2747 case JOIN_OP_REPLACE:
2748 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2750 default:
2751 ERR("unhandled join op %u\n", op);
2752 return NULL;
2756 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2757 BYTE *new_value, DWORD new_size, DWORD *size )
2759 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2760 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2761 enum join_op op = JOIN_OP_REPLACE;
2762 WCHAR **old = NULL, **new = NULL;
2763 BYTE *ret;
2765 if (new_size / sizeof(WCHAR) - 1 > 1)
2767 new_ptr = (const WCHAR *)new_value;
2768 new_len = new_size / sizeof(WCHAR) - 1;
2770 if (!new_ptr[0] && new_ptr[new_len - 1])
2772 op = JOIN_OP_APPEND;
2773 new_len--;
2774 new_ptr++;
2776 else if (new_ptr[0] && !new_ptr[new_len - 1])
2778 op = JOIN_OP_PREPEND;
2779 new_len--;
2781 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2783 op = JOIN_OP_REPLACE;
2784 new_len -= 2;
2785 new_ptr++;
2787 new = split_multi_string_values( new_ptr, new_len, &new_count );
2789 if (old_size / sizeof(WCHAR) - 1 > 1)
2791 old_ptr = (const WCHAR *)old_value;
2792 old_len = old_size / sizeof(WCHAR) - 1;
2793 old = split_multi_string_values( old_ptr, old_len, &old_count );
2795 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2796 for (i = 0; i < old_count; i++) msi_free( old[i] );
2797 for (i = 0; i < new_count; i++) msi_free( new[i] );
2798 msi_free( old );
2799 msi_free( new );
2800 return ret;
2803 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2805 BYTE *ret;
2806 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2807 if (!(ret = msi_alloc( *size ))) return NULL;
2808 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2809 return ret;
2812 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2814 MSIPACKAGE *package = param;
2815 BYTE *new_value, *old_value = NULL;
2816 HKEY root_key, hkey;
2817 DWORD type, old_type, new_size, old_size = 0;
2818 LPWSTR deformated, uikey, keypath;
2819 const WCHAR *szRoot, *component, *name, *key, *str;
2820 MSICOMPONENT *comp;
2821 MSIRECORD * uirow;
2822 INT root;
2823 BOOL check_first = FALSE;
2824 int len;
2826 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2828 component = MSI_RecordGetString(row, 6);
2829 comp = msi_get_loaded_component(package,component);
2830 if (!comp)
2831 return ERROR_SUCCESS;
2833 comp->Action = msi_get_component_action( package, comp );
2834 if (comp->Action != INSTALLSTATE_LOCAL)
2836 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2837 return ERROR_SUCCESS;
2840 name = MSI_RecordGetString(row, 4);
2841 if( MSI_RecordIsNull(row,5) && name )
2843 /* null values can have special meanings */
2844 if (name[0]=='-' && name[1] == 0)
2845 return ERROR_SUCCESS;
2846 if ((name[0] == '+' || name[0] == '*') && !name[1])
2847 check_first = TRUE;
2850 root = MSI_RecordGetInteger(row,2);
2851 key = MSI_RecordGetString(row, 3);
2853 szRoot = get_root_key( package, root, &root_key );
2854 if (!szRoot)
2855 return ERROR_SUCCESS;
2857 deformat_string(package, key , &deformated);
2858 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2859 strcpyW(uikey,szRoot);
2860 strcatW(uikey,deformated);
2862 keypath = get_keypath( comp, root_key, deformated );
2863 msi_free( deformated );
2864 if (!(hkey = open_key( root_key, keypath, TRUE )))
2866 ERR("Could not create key %s\n", debugstr_w(keypath));
2867 msi_free(uikey);
2868 msi_free(keypath);
2869 return ERROR_FUNCTION_FAILED;
2871 str = msi_record_get_string( row, 5, &len );
2872 if (str && len > strlenW( str ))
2874 type = REG_MULTI_SZ;
2875 new_size = (len + 1) * sizeof(WCHAR);
2876 new_value = (BYTE *)msi_strdupW( str, len );
2878 else new_value = parse_value( package, str, &type, &new_size );
2879 deformat_string(package, name, &deformated);
2881 if (!is_special_entry( name ))
2883 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2884 if (type == REG_MULTI_SZ)
2886 BYTE *new;
2887 if (old_value && old_type != REG_MULTI_SZ)
2889 msi_free( old_value );
2890 old_value = NULL;
2891 old_size = 0;
2893 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2894 msi_free( new_value );
2895 new_value = new;
2897 if (!check_first)
2899 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2900 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2902 else if (!old_value)
2904 if (deformated || new_size)
2906 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2907 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2910 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2912 RegCloseKey(hkey);
2914 uirow = MSI_CreateRecord(3);
2915 MSI_RecordSetStringW(uirow,2,deformated);
2916 MSI_RecordSetStringW(uirow,1,uikey);
2917 if (type == REG_SZ || type == REG_EXPAND_SZ)
2918 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2919 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2920 msiobj_release( &uirow->hdr );
2922 msi_free(new_value);
2923 msi_free(old_value);
2924 msi_free(deformated);
2925 msi_free(uikey);
2926 msi_free(keypath);
2928 return ERROR_SUCCESS;
2931 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2933 static const WCHAR query[] = {
2934 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2935 '`','R','e','g','i','s','t','r','y','`',0};
2936 MSIQUERY *view;
2937 UINT rc;
2939 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2940 if (rc != ERROR_SUCCESS)
2941 return ERROR_SUCCESS;
2943 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2944 msiobj_release(&view->hdr);
2945 return rc;
2948 static void delete_key( HKEY root, const WCHAR *path )
2950 REGSAM access = 0;
2951 WCHAR *subkey, *p;
2952 HKEY hkey;
2953 LONG res;
2955 if (is_wow64) access |= KEY_WOW64_64KEY;
2957 if (!(subkey = strdupW( path ))) return;
2958 for (;;)
2960 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2961 hkey = open_key( root, subkey, FALSE );
2962 if (!hkey) break;
2963 if (p && p[1])
2964 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2965 else
2966 res = RegDeleteKeyExW( root, subkey, access, 0 );
2967 if (res)
2969 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2970 break;
2972 if (p && p[1]) RegCloseKey( hkey );
2973 else break;
2975 msi_free( subkey );
2978 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2980 LONG res;
2981 HKEY hkey;
2982 DWORD num_subkeys, num_values;
2984 if ((hkey = open_key( root, path, FALSE )))
2986 if ((res = RegDeleteValueW( hkey, value )))
2987 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2989 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2990 NULL, NULL, NULL, NULL );
2991 RegCloseKey( hkey );
2992 if (!res && !num_subkeys && !num_values)
2994 TRACE("removing empty key %s\n", debugstr_w(path));
2995 delete_key( root, path );
3000 static void delete_tree( HKEY root, const WCHAR *path )
3002 LONG res;
3003 HKEY hkey;
3005 if (!(hkey = open_key( root, path, FALSE ))) return;
3006 res = RegDeleteTreeW( hkey, NULL );
3007 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3008 delete_key( root, path );
3009 RegCloseKey( hkey );
3012 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3014 MSIPACKAGE *package = param;
3015 LPCWSTR component, name, key_str, root_key_str;
3016 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3017 MSICOMPONENT *comp;
3018 MSIRECORD *uirow;
3019 BOOL delete_key = FALSE;
3020 HKEY hkey_root;
3021 UINT size;
3022 INT root;
3024 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3026 component = MSI_RecordGetString( row, 6 );
3027 comp = msi_get_loaded_component( package, component );
3028 if (!comp)
3029 return ERROR_SUCCESS;
3031 comp->Action = msi_get_component_action( package, comp );
3032 if (comp->Action != INSTALLSTATE_ABSENT)
3034 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3035 return ERROR_SUCCESS;
3038 name = MSI_RecordGetString( row, 4 );
3039 if (MSI_RecordIsNull( row, 5 ) && name )
3041 if (name[0] == '+' && !name[1])
3042 return ERROR_SUCCESS;
3043 if ((name[0] == '-' || name[0] == '*') && !name[1])
3045 delete_key = TRUE;
3046 name = NULL;
3050 root = MSI_RecordGetInteger( row, 2 );
3051 key_str = MSI_RecordGetString( row, 3 );
3053 root_key_str = get_root_key( package, root, &hkey_root );
3054 if (!root_key_str)
3055 return ERROR_SUCCESS;
3057 deformat_string( package, key_str, &deformated_key );
3058 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3059 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3060 strcpyW( ui_key_str, root_key_str );
3061 strcatW( ui_key_str, deformated_key );
3063 deformat_string( package, name, &deformated_name );
3065 keypath = get_keypath( comp, hkey_root, deformated_key );
3066 msi_free( deformated_key );
3067 if (delete_key) delete_tree( hkey_root, keypath );
3068 else delete_value( hkey_root, keypath, deformated_name );
3069 msi_free( keypath );
3071 uirow = MSI_CreateRecord( 2 );
3072 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3073 MSI_RecordSetStringW( uirow, 2, deformated_name );
3074 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3075 msiobj_release( &uirow->hdr );
3077 msi_free( ui_key_str );
3078 msi_free( deformated_name );
3079 return ERROR_SUCCESS;
3082 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3084 MSIPACKAGE *package = param;
3085 LPCWSTR component, name, key_str, root_key_str;
3086 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3087 MSICOMPONENT *comp;
3088 MSIRECORD *uirow;
3089 BOOL delete_key = FALSE;
3090 HKEY hkey_root;
3091 UINT size;
3092 INT root;
3094 component = MSI_RecordGetString( row, 5 );
3095 comp = msi_get_loaded_component( package, component );
3096 if (!comp)
3097 return ERROR_SUCCESS;
3099 comp->Action = msi_get_component_action( package, comp );
3100 if (comp->Action != INSTALLSTATE_LOCAL)
3102 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3103 return ERROR_SUCCESS;
3106 if ((name = MSI_RecordGetString( row, 4 )))
3108 if (name[0] == '-' && !name[1])
3110 delete_key = TRUE;
3111 name = NULL;
3115 root = MSI_RecordGetInteger( row, 2 );
3116 key_str = MSI_RecordGetString( row, 3 );
3118 root_key_str = get_root_key( package, root, &hkey_root );
3119 if (!root_key_str)
3120 return ERROR_SUCCESS;
3122 deformat_string( package, key_str, &deformated_key );
3123 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3124 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3125 strcpyW( ui_key_str, root_key_str );
3126 strcatW( ui_key_str, deformated_key );
3128 deformat_string( package, name, &deformated_name );
3130 keypath = get_keypath( comp, hkey_root, deformated_key );
3131 msi_free( deformated_key );
3132 if (delete_key) delete_tree( hkey_root, keypath );
3133 else delete_value( hkey_root, keypath, deformated_name );
3134 msi_free( keypath );
3136 uirow = MSI_CreateRecord( 2 );
3137 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3138 MSI_RecordSetStringW( uirow, 2, deformated_name );
3139 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3140 msiobj_release( &uirow->hdr );
3142 msi_free( ui_key_str );
3143 msi_free( deformated_name );
3144 return ERROR_SUCCESS;
3147 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3149 static const WCHAR registry_query[] = {
3150 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3151 '`','R','e','g','i','s','t','r','y','`',0};
3152 static const WCHAR remove_registry_query[] = {
3153 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3154 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3155 MSIQUERY *view;
3156 UINT rc;
3158 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3159 if (rc == ERROR_SUCCESS)
3161 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3162 msiobj_release( &view->hdr );
3163 if (rc != ERROR_SUCCESS)
3164 return rc;
3166 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3167 if (rc == ERROR_SUCCESS)
3169 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3170 msiobj_release( &view->hdr );
3171 if (rc != ERROR_SUCCESS)
3172 return rc;
3174 return ERROR_SUCCESS;
3177 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3179 package->script->CurrentlyScripting = TRUE;
3181 return ERROR_SUCCESS;
3185 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3187 static const WCHAR query[]= {
3188 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3189 '`','R','e','g','i','s','t','r','y','`',0};
3190 MSICOMPONENT *comp;
3191 DWORD total = 0, count = 0;
3192 MSIQUERY *view;
3193 MSIFEATURE *feature;
3194 MSIFILE *file;
3195 UINT rc;
3197 TRACE("InstallValidate\n");
3199 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3200 if (rc == ERROR_SUCCESS)
3202 rc = MSI_IterateRecords( view, &count, NULL, package );
3203 msiobj_release( &view->hdr );
3204 if (rc != ERROR_SUCCESS)
3205 return rc;
3206 total += count * REG_PROGRESS_VALUE;
3208 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3209 total += COMPONENT_PROGRESS_VALUE;
3211 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3212 total += file->FileSize;
3214 msi_ui_progress( package, 0, total, 0, 0 );
3216 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3218 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3219 debugstr_w(feature->Feature), feature->Installed,
3220 feature->ActionRequest, feature->Action);
3222 return ERROR_SUCCESS;
3225 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3227 MSIPACKAGE* package = param;
3228 LPCWSTR cond = NULL;
3229 LPCWSTR message = NULL;
3230 UINT r;
3232 static const WCHAR title[]=
3233 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3235 cond = MSI_RecordGetString(row,1);
3237 r = MSI_EvaluateConditionW(package,cond);
3238 if (r == MSICONDITION_FALSE)
3240 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3242 LPWSTR deformated;
3243 message = MSI_RecordGetString(row,2);
3244 deformat_string(package,message,&deformated);
3245 MessageBoxW(NULL,deformated,title,MB_OK);
3246 msi_free(deformated);
3249 return ERROR_INSTALL_FAILURE;
3252 return ERROR_SUCCESS;
3255 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3257 static const WCHAR query[] = {
3258 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3259 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3260 MSIQUERY *view;
3261 UINT rc;
3263 TRACE("Checking launch conditions\n");
3265 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3266 if (rc != ERROR_SUCCESS)
3267 return ERROR_SUCCESS;
3269 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3270 msiobj_release(&view->hdr);
3271 return rc;
3274 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3277 if (!cmp->KeyPath)
3278 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3280 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3282 static const WCHAR query[] = {
3283 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3284 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3285 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3286 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3287 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3288 MSIRECORD *row;
3289 UINT root, len;
3290 LPWSTR deformated, buffer, deformated_name;
3291 LPCWSTR key, name;
3293 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3294 if (!row)
3295 return NULL;
3297 root = MSI_RecordGetInteger(row,2);
3298 key = MSI_RecordGetString(row, 3);
3299 name = MSI_RecordGetString(row, 4);
3300 deformat_string(package, key , &deformated);
3301 deformat_string(package, name, &deformated_name);
3303 len = strlenW(deformated) + 6;
3304 if (deformated_name)
3305 len+=strlenW(deformated_name);
3307 buffer = msi_alloc( len *sizeof(WCHAR));
3309 if (deformated_name)
3310 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3311 else
3312 sprintfW(buffer,fmt,root,deformated);
3314 msi_free(deformated);
3315 msi_free(deformated_name);
3316 msiobj_release(&row->hdr);
3318 return buffer;
3320 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3322 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3323 return NULL;
3325 else
3327 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3329 if (file)
3330 return strdupW( file->TargetPath );
3332 return NULL;
3335 static HKEY openSharedDLLsKey(void)
3337 HKEY hkey=0;
3338 static const WCHAR path[] =
3339 {'S','o','f','t','w','a','r','e','\\',
3340 'M','i','c','r','o','s','o','f','t','\\',
3341 'W','i','n','d','o','w','s','\\',
3342 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3343 'S','h','a','r','e','d','D','L','L','s',0};
3345 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3346 return hkey;
3349 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3351 HKEY hkey;
3352 DWORD count=0;
3353 DWORD type;
3354 DWORD sz = sizeof(count);
3355 DWORD rc;
3357 hkey = openSharedDLLsKey();
3358 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3359 if (rc != ERROR_SUCCESS)
3360 count = 0;
3361 RegCloseKey(hkey);
3362 return count;
3365 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3367 HKEY hkey;
3369 hkey = openSharedDLLsKey();
3370 if (count > 0)
3371 msi_reg_set_val_dword( hkey, path, count );
3372 else
3373 RegDeleteValueW(hkey,path);
3374 RegCloseKey(hkey);
3375 return count;
3378 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3380 MSIFEATURE *feature;
3381 INT count = 0;
3382 BOOL write = FALSE;
3384 /* only refcount DLLs */
3385 if (comp->KeyPath == NULL ||
3386 comp->assembly ||
3387 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3388 comp->Attributes & msidbComponentAttributesODBCDataSource)
3389 write = FALSE;
3390 else
3392 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3393 write = (count > 0);
3395 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3396 write = TRUE;
3399 /* increment counts */
3400 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3402 ComponentList *cl;
3404 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3405 continue;
3407 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3409 if ( cl->component == comp )
3410 count++;
3414 /* decrement counts */
3415 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3417 ComponentList *cl;
3419 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3420 continue;
3422 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3424 if ( cl->component == comp )
3425 count--;
3429 /* ref count all the files in the component */
3430 if (write)
3432 MSIFILE *file;
3434 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3436 if (file->Component == comp)
3437 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3441 /* add a count for permanent */
3442 if (comp->Attributes & msidbComponentAttributesPermanent)
3443 count ++;
3445 comp->RefCount = count;
3447 if (write)
3448 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3451 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3453 if (comp->assembly)
3455 const WCHAR prefixW[] = {'<','\\',0};
3456 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3457 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3459 if (keypath)
3461 strcpyW( keypath, prefixW );
3462 strcatW( keypath, comp->assembly->display_name );
3464 return keypath;
3466 return resolve_keypath( package, comp );
3469 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3471 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3472 UINT rc;
3473 MSICOMPONENT *comp;
3474 HKEY hkey;
3476 TRACE("\n");
3478 squash_guid(package->ProductCode,squished_pc);
3479 msi_set_sourcedir_props(package, FALSE);
3481 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3483 MSIRECORD *uirow;
3484 INSTALLSTATE action;
3486 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3487 if (!comp->ComponentId)
3488 continue;
3490 squash_guid( comp->ComponentId, squished_cc );
3491 msi_free( comp->FullKeypath );
3492 comp->FullKeypath = build_full_keypath( package, comp );
3494 ACTION_RefCountComponent( package, comp );
3496 if (package->need_rollback) action = comp->Installed;
3497 else action = comp->ActionRequest;
3499 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3500 debugstr_w(comp->Component), debugstr_w(squished_cc),
3501 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3503 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3505 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3506 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3507 else
3508 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3510 if (rc != ERROR_SUCCESS)
3511 continue;
3513 if (comp->Attributes & msidbComponentAttributesPermanent)
3515 static const WCHAR szPermKey[] =
3516 { '0','0','0','0','0','0','0','0','0','0','0','0',
3517 '0','0','0','0','0','0','0','0','0','0','0','0',
3518 '0','0','0','0','0','0','0','0',0 };
3520 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3522 if (action == INSTALLSTATE_LOCAL)
3523 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3524 else
3526 MSIFILE *file;
3527 MSIRECORD *row;
3528 LPWSTR ptr, ptr2;
3529 WCHAR source[MAX_PATH];
3530 WCHAR base[MAX_PATH];
3531 LPWSTR sourcepath;
3533 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3534 static const WCHAR query[] = {
3535 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3536 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3537 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3538 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3539 '`','D','i','s','k','I','d','`',0};
3541 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3542 continue;
3544 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3545 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3546 ptr2 = strrchrW(source, '\\') + 1;
3547 msiobj_release(&row->hdr);
3549 lstrcpyW(base, package->PackagePath);
3550 ptr = strrchrW(base, '\\');
3551 *(ptr + 1) = '\0';
3553 sourcepath = msi_resolve_file_source(package, file);
3554 ptr = sourcepath + lstrlenW(base);
3555 lstrcpyW(ptr2, ptr);
3556 msi_free(sourcepath);
3558 msi_reg_set_val_str(hkey, squished_pc, source);
3560 RegCloseKey(hkey);
3562 else if (action == INSTALLSTATE_ABSENT)
3564 if (comp->num_clients <= 0)
3566 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3567 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3568 else
3569 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3573 /* UI stuff */
3574 uirow = MSI_CreateRecord(3);
3575 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3576 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3577 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3578 msi_ui_actiondata( package, szProcessComponents, uirow );
3579 msiobj_release( &uirow->hdr );
3581 return ERROR_SUCCESS;
3584 typedef struct {
3585 CLSID clsid;
3586 LPWSTR source;
3588 LPWSTR path;
3589 ITypeLib *ptLib;
3590 } typelib_struct;
3592 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3593 LPWSTR lpszName, LONG_PTR lParam)
3595 TLIBATTR *attr;
3596 typelib_struct *tl_struct = (typelib_struct*) lParam;
3597 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3598 int sz;
3599 HRESULT res;
3601 if (!IS_INTRESOURCE(lpszName))
3603 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3604 return TRUE;
3607 sz = strlenW(tl_struct->source)+4;
3608 sz *= sizeof(WCHAR);
3610 if ((INT_PTR)lpszName == 1)
3611 tl_struct->path = strdupW(tl_struct->source);
3612 else
3614 tl_struct->path = msi_alloc(sz);
3615 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3618 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3619 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3620 if (FAILED(res))
3622 msi_free(tl_struct->path);
3623 tl_struct->path = NULL;
3625 return TRUE;
3628 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3629 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3631 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3632 return FALSE;
3635 msi_free(tl_struct->path);
3636 tl_struct->path = NULL;
3638 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3639 ITypeLib_Release(tl_struct->ptLib);
3641 return TRUE;
3644 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3646 MSIPACKAGE* package = param;
3647 LPCWSTR component;
3648 MSICOMPONENT *comp;
3649 MSIFILE *file;
3650 typelib_struct tl_struct;
3651 ITypeLib *tlib;
3652 HMODULE module;
3653 HRESULT hr;
3655 component = MSI_RecordGetString(row,3);
3656 comp = msi_get_loaded_component(package,component);
3657 if (!comp)
3658 return ERROR_SUCCESS;
3660 comp->Action = msi_get_component_action( package, comp );
3661 if (comp->Action != INSTALLSTATE_LOCAL)
3663 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3664 return ERROR_SUCCESS;
3667 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3669 TRACE("component has no key path\n");
3670 return ERROR_SUCCESS;
3672 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3674 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3675 if (module)
3677 LPCWSTR guid;
3678 guid = MSI_RecordGetString(row,1);
3679 CLSIDFromString( guid, &tl_struct.clsid);
3680 tl_struct.source = strdupW( file->TargetPath );
3681 tl_struct.path = NULL;
3683 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3684 (LONG_PTR)&tl_struct);
3686 if (tl_struct.path)
3688 LPCWSTR helpid, help_path = NULL;
3689 HRESULT res;
3691 helpid = MSI_RecordGetString(row,6);
3693 if (helpid) help_path = msi_get_target_folder( package, helpid );
3694 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3696 if (FAILED(res))
3697 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3698 else
3699 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3701 ITypeLib_Release(tl_struct.ptLib);
3702 msi_free(tl_struct.path);
3704 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3706 FreeLibrary(module);
3707 msi_free(tl_struct.source);
3709 else
3711 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3712 if (FAILED(hr))
3714 ERR("Failed to load type library: %08x\n", hr);
3715 return ERROR_INSTALL_FAILURE;
3718 ITypeLib_Release(tlib);
3721 return ERROR_SUCCESS;
3724 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3726 static const WCHAR query[] = {
3727 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3728 '`','T','y','p','e','L','i','b','`',0};
3729 MSIQUERY *view;
3730 UINT rc;
3732 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3733 if (rc != ERROR_SUCCESS)
3734 return ERROR_SUCCESS;
3736 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3737 msiobj_release(&view->hdr);
3738 return rc;
3741 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3743 MSIPACKAGE *package = param;
3744 LPCWSTR component, guid;
3745 MSICOMPONENT *comp;
3746 GUID libid;
3747 UINT version;
3748 LCID language;
3749 SYSKIND syskind;
3750 HRESULT hr;
3752 component = MSI_RecordGetString( row, 3 );
3753 comp = msi_get_loaded_component( package, component );
3754 if (!comp)
3755 return ERROR_SUCCESS;
3757 comp->Action = msi_get_component_action( package, comp );
3758 if (comp->Action != INSTALLSTATE_ABSENT)
3760 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3761 return ERROR_SUCCESS;
3763 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3765 guid = MSI_RecordGetString( row, 1 );
3766 CLSIDFromString( guid, &libid );
3767 version = MSI_RecordGetInteger( row, 4 );
3768 language = MSI_RecordGetInteger( row, 2 );
3770 #ifdef _WIN64
3771 syskind = SYS_WIN64;
3772 #else
3773 syskind = SYS_WIN32;
3774 #endif
3776 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3777 if (FAILED(hr))
3779 WARN("Failed to unregister typelib: %08x\n", hr);
3782 return ERROR_SUCCESS;
3785 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3787 static const WCHAR query[] = {
3788 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3789 '`','T','y','p','e','L','i','b','`',0};
3790 MSIQUERY *view;
3791 UINT rc;
3793 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3794 if (rc != ERROR_SUCCESS)
3795 return ERROR_SUCCESS;
3797 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3798 msiobj_release( &view->hdr );
3799 return rc;
3802 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3804 static const WCHAR szlnk[] = {'.','l','n','k',0};
3805 LPCWSTR directory, extension, link_folder;
3806 LPWSTR link_file, filename;
3808 directory = MSI_RecordGetString( row, 2 );
3809 link_folder = msi_get_target_folder( package, directory );
3810 if (!link_folder)
3812 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3813 return NULL;
3815 /* may be needed because of a bug somewhere else */
3816 msi_create_full_path( link_folder );
3818 filename = msi_dup_record_field( row, 3 );
3819 msi_reduce_to_long_filename( filename );
3821 extension = strchrW( filename, '.' );
3822 if (!extension || strcmpiW( extension, szlnk ))
3824 int len = strlenW( filename );
3825 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3826 memcpy( filename + len, szlnk, sizeof(szlnk) );
3828 link_file = msi_build_directory_name( 2, link_folder, filename );
3829 msi_free( filename );
3831 return link_file;
3834 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3836 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3837 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3838 WCHAR *folder, *dest, *path;
3840 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3841 folder = msi_dup_property( package->db, szWindowsFolder );
3842 else
3844 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3845 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3846 msi_free( appdata );
3848 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3849 msi_create_full_path( dest );
3850 path = msi_build_directory_name( 2, dest, icon_name );
3851 msi_free( folder );
3852 msi_free( dest );
3853 return path;
3856 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3858 MSIPACKAGE *package = param;
3859 LPWSTR link_file, deformated, path;
3860 LPCWSTR component, target;
3861 MSICOMPONENT *comp;
3862 IShellLinkW *sl = NULL;
3863 IPersistFile *pf = NULL;
3864 HRESULT res;
3866 component = MSI_RecordGetString(row, 4);
3867 comp = msi_get_loaded_component(package, component);
3868 if (!comp)
3869 return ERROR_SUCCESS;
3871 comp->Action = msi_get_component_action( package, comp );
3872 if (comp->Action != INSTALLSTATE_LOCAL)
3874 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3875 return ERROR_SUCCESS;
3877 msi_ui_actiondata( package, szCreateShortcuts, row );
3879 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3880 &IID_IShellLinkW, (LPVOID *) &sl );
3882 if (FAILED( res ))
3884 ERR("CLSID_ShellLink not available\n");
3885 goto err;
3888 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3889 if (FAILED( res ))
3891 ERR("QueryInterface(IID_IPersistFile) failed\n");
3892 goto err;
3895 target = MSI_RecordGetString(row, 5);
3896 if (strchrW(target, '['))
3898 deformat_string( package, target, &path );
3899 TRACE("target path is %s\n", debugstr_w(path));
3900 IShellLinkW_SetPath( sl, path );
3901 msi_free( path );
3903 else
3905 FIXME("poorly handled shortcut format, advertised shortcut\n");
3906 IShellLinkW_SetPath(sl,comp->FullKeypath);
3909 if (!MSI_RecordIsNull(row,6))
3911 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3912 deformat_string(package, arguments, &deformated);
3913 IShellLinkW_SetArguments(sl,deformated);
3914 msi_free(deformated);
3917 if (!MSI_RecordIsNull(row,7))
3919 LPCWSTR description = MSI_RecordGetString(row, 7);
3920 IShellLinkW_SetDescription(sl, description);
3923 if (!MSI_RecordIsNull(row,8))
3924 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3926 if (!MSI_RecordIsNull(row,9))
3928 INT index;
3929 LPCWSTR icon = MSI_RecordGetString(row, 9);
3931 path = msi_build_icon_path(package, icon);
3932 index = MSI_RecordGetInteger(row,10);
3934 /* no value means 0 */
3935 if (index == MSI_NULL_INTEGER)
3936 index = 0;
3938 IShellLinkW_SetIconLocation(sl, path, index);
3939 msi_free(path);
3942 if (!MSI_RecordIsNull(row,11))
3943 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3945 if (!MSI_RecordIsNull(row,12))
3947 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3948 full_path = msi_get_target_folder( package, wkdir );
3949 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3951 link_file = get_link_file(package, row);
3953 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3954 IPersistFile_Save(pf, link_file, FALSE);
3955 msi_free(link_file);
3957 err:
3958 if (pf)
3959 IPersistFile_Release( pf );
3960 if (sl)
3961 IShellLinkW_Release( sl );
3963 return ERROR_SUCCESS;
3966 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3968 static const WCHAR query[] = {
3969 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3970 '`','S','h','o','r','t','c','u','t','`',0};
3971 MSIQUERY *view;
3972 HRESULT res;
3973 UINT rc;
3975 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3976 if (rc != ERROR_SUCCESS)
3977 return ERROR_SUCCESS;
3979 res = CoInitialize( NULL );
3981 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3982 msiobj_release(&view->hdr);
3984 if (SUCCEEDED(res)) CoUninitialize();
3985 return rc;
3988 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3990 MSIPACKAGE *package = param;
3991 LPWSTR link_file;
3992 LPCWSTR component;
3993 MSICOMPONENT *comp;
3995 component = MSI_RecordGetString( row, 4 );
3996 comp = msi_get_loaded_component( package, component );
3997 if (!comp)
3998 return ERROR_SUCCESS;
4000 comp->Action = msi_get_component_action( package, comp );
4001 if (comp->Action != INSTALLSTATE_ABSENT)
4003 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4004 return ERROR_SUCCESS;
4006 msi_ui_actiondata( package, szRemoveShortcuts, row );
4008 link_file = get_link_file( package, row );
4010 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4011 if (!DeleteFileW( link_file ))
4013 WARN("Failed to remove shortcut file %u\n", GetLastError());
4015 msi_free( link_file );
4017 return ERROR_SUCCESS;
4020 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4022 static const WCHAR query[] = {
4023 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4024 '`','S','h','o','r','t','c','u','t','`',0};
4025 MSIQUERY *view;
4026 UINT rc;
4028 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4029 if (rc != ERROR_SUCCESS)
4030 return ERROR_SUCCESS;
4032 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4033 msiobj_release( &view->hdr );
4034 return rc;
4037 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4039 MSIPACKAGE* package = param;
4040 HANDLE the_file;
4041 LPWSTR FilePath;
4042 LPCWSTR FileName;
4043 CHAR buffer[1024];
4044 DWORD sz;
4045 UINT rc;
4047 FileName = MSI_RecordGetString(row,1);
4048 if (!FileName)
4050 ERR("Unable to get FileName\n");
4051 return ERROR_SUCCESS;
4054 FilePath = msi_build_icon_path(package, FileName);
4056 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4058 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4059 FILE_ATTRIBUTE_NORMAL, NULL);
4061 if (the_file == INVALID_HANDLE_VALUE)
4063 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4064 msi_free(FilePath);
4065 return ERROR_SUCCESS;
4070 DWORD write;
4071 sz = 1024;
4072 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4073 if (rc != ERROR_SUCCESS)
4075 ERR("Failed to get stream\n");
4076 CloseHandle(the_file);
4077 DeleteFileW(FilePath);
4078 break;
4080 WriteFile(the_file,buffer,sz,&write,NULL);
4081 } while (sz == 1024);
4083 msi_free(FilePath);
4084 CloseHandle(the_file);
4086 return ERROR_SUCCESS;
4089 static UINT msi_publish_icons(MSIPACKAGE *package)
4091 static const WCHAR query[]= {
4092 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4093 '`','I','c','o','n','`',0};
4094 MSIQUERY *view;
4095 UINT r;
4097 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4098 if (r == ERROR_SUCCESS)
4100 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4101 msiobj_release(&view->hdr);
4102 if (r != ERROR_SUCCESS)
4103 return r;
4105 return ERROR_SUCCESS;
4108 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4110 UINT r;
4111 HKEY source;
4112 LPWSTR buffer;
4113 MSIMEDIADISK *disk;
4114 MSISOURCELISTINFO *info;
4116 r = RegCreateKeyW(hkey, szSourceList, &source);
4117 if (r != ERROR_SUCCESS)
4118 return r;
4120 RegCloseKey(source);
4122 buffer = strrchrW(package->PackagePath, '\\') + 1;
4123 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4124 package->Context, MSICODE_PRODUCT,
4125 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4126 if (r != ERROR_SUCCESS)
4127 return r;
4129 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4130 package->Context, MSICODE_PRODUCT,
4131 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4132 if (r != ERROR_SUCCESS)
4133 return r;
4135 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4136 package->Context, MSICODE_PRODUCT,
4137 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4138 if (r != ERROR_SUCCESS)
4139 return r;
4141 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4143 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4144 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4145 info->options, info->value);
4146 else
4147 MsiSourceListSetInfoW(package->ProductCode, NULL,
4148 info->context, info->options,
4149 info->property, info->value);
4152 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4154 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4155 disk->context, disk->options,
4156 disk->disk_id, disk->volume_label, disk->disk_prompt);
4159 return ERROR_SUCCESS;
4162 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4164 MSIHANDLE hdb, suminfo;
4165 WCHAR guids[MAX_PATH];
4166 WCHAR packcode[SQUISH_GUID_SIZE];
4167 LPWSTR buffer;
4168 LPWSTR ptr;
4169 DWORD langid;
4170 DWORD size;
4171 UINT r;
4173 static const WCHAR szARPProductIcon[] =
4174 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4175 static const WCHAR szAssignment[] =
4176 {'A','s','s','i','g','n','m','e','n','t',0};
4177 static const WCHAR szAdvertiseFlags[] =
4178 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4179 static const WCHAR szClients[] =
4180 {'C','l','i','e','n','t','s',0};
4181 static const WCHAR szColon[] = {':',0};
4183 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4184 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4185 msi_free(buffer);
4187 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4188 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4190 /* FIXME */
4191 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4193 buffer = msi_dup_property(package->db, szARPProductIcon);
4194 if (buffer)
4196 LPWSTR path = msi_build_icon_path(package, buffer);
4197 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4198 msi_free(path);
4199 msi_free(buffer);
4202 buffer = msi_dup_property(package->db, szProductVersion);
4203 if (buffer)
4205 DWORD verdword = msi_version_str_to_dword(buffer);
4206 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4207 msi_free(buffer);
4210 msi_reg_set_val_dword(hkey, szAssignment, 0);
4211 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4212 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4213 msi_reg_set_val_str(hkey, szClients, szColon);
4215 hdb = alloc_msihandle(&package->db->hdr);
4216 if (!hdb)
4217 return ERROR_NOT_ENOUGH_MEMORY;
4219 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4220 MsiCloseHandle(hdb);
4221 if (r != ERROR_SUCCESS)
4222 goto done;
4224 size = MAX_PATH;
4225 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4226 NULL, guids, &size);
4227 if (r != ERROR_SUCCESS)
4228 goto done;
4230 ptr = strchrW(guids, ';');
4231 if (ptr) *ptr = 0;
4232 squash_guid(guids, packcode);
4233 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4235 done:
4236 MsiCloseHandle(suminfo);
4237 return ERROR_SUCCESS;
4240 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4242 UINT r;
4243 HKEY hkey;
4244 LPWSTR upgrade;
4245 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4247 upgrade = msi_dup_property(package->db, szUpgradeCode);
4248 if (!upgrade)
4249 return ERROR_SUCCESS;
4251 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4252 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4253 else
4254 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4256 if (r != ERROR_SUCCESS)
4258 WARN("failed to open upgrade code key\n");
4259 msi_free(upgrade);
4260 return ERROR_SUCCESS;
4262 squash_guid(package->ProductCode, squashed_pc);
4263 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4264 RegCloseKey(hkey);
4265 msi_free(upgrade);
4266 return ERROR_SUCCESS;
4269 static BOOL msi_check_publish(MSIPACKAGE *package)
4271 MSIFEATURE *feature;
4273 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4275 feature->Action = msi_get_feature_action( package, feature );
4276 if (feature->Action == INSTALLSTATE_LOCAL)
4277 return TRUE;
4280 return FALSE;
4283 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4285 MSIFEATURE *feature;
4287 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4289 feature->Action = msi_get_feature_action( package, feature );
4290 if (feature->Action != INSTALLSTATE_ABSENT)
4291 return FALSE;
4294 return TRUE;
4297 static UINT msi_publish_patches( MSIPACKAGE *package )
4299 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4300 WCHAR patch_squashed[GUID_SIZE];
4301 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4302 LONG res;
4303 MSIPATCHINFO *patch;
4304 UINT r;
4305 WCHAR *p, *all_patches = NULL;
4306 DWORD len = 0;
4308 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4309 if (r != ERROR_SUCCESS)
4310 return ERROR_FUNCTION_FAILED;
4312 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4313 if (res != ERROR_SUCCESS)
4315 r = ERROR_FUNCTION_FAILED;
4316 goto done;
4319 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4320 if (r != ERROR_SUCCESS)
4321 goto done;
4323 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4325 squash_guid( patch->patchcode, patch_squashed );
4326 len += strlenW( patch_squashed ) + 1;
4329 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4330 if (!all_patches)
4331 goto done;
4333 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4335 HKEY patch_key;
4337 squash_guid( patch->patchcode, p );
4338 p += strlenW( p ) + 1;
4340 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4341 (const BYTE *)patch->transforms,
4342 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4343 if (res != ERROR_SUCCESS)
4344 goto done;
4346 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4347 if (r != ERROR_SUCCESS)
4348 goto done;
4350 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4351 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4352 RegCloseKey( patch_key );
4353 if (res != ERROR_SUCCESS)
4354 goto done;
4356 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4358 res = GetLastError();
4359 ERR("Unable to copy patch package %d\n", res);
4360 goto done;
4362 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4363 if (res != ERROR_SUCCESS)
4364 goto done;
4366 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4367 RegCloseKey( patch_key );
4368 if (res != ERROR_SUCCESS)
4369 goto done;
4372 all_patches[len] = 0;
4373 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4374 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4375 if (res != ERROR_SUCCESS)
4376 goto done;
4378 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4379 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4380 if (res != ERROR_SUCCESS)
4381 r = ERROR_FUNCTION_FAILED;
4383 done:
4384 RegCloseKey( product_patches_key );
4385 RegCloseKey( patches_key );
4386 RegCloseKey( product_key );
4387 msi_free( all_patches );
4388 return r;
4391 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4393 UINT rc;
4394 HKEY hukey = NULL, hudkey = NULL;
4395 MSIRECORD *uirow;
4397 if (!list_empty(&package->patches))
4399 rc = msi_publish_patches(package);
4400 if (rc != ERROR_SUCCESS)
4401 goto end;
4404 /* FIXME: also need to publish if the product is in advertise mode */
4405 if (!msi_check_publish(package))
4406 return ERROR_SUCCESS;
4408 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4409 &hukey, TRUE);
4410 if (rc != ERROR_SUCCESS)
4411 goto end;
4413 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4414 NULL, &hudkey, TRUE);
4415 if (rc != ERROR_SUCCESS)
4416 goto end;
4418 rc = msi_publish_upgrade_code(package);
4419 if (rc != ERROR_SUCCESS)
4420 goto end;
4422 rc = msi_publish_product_properties(package, hukey);
4423 if (rc != ERROR_SUCCESS)
4424 goto end;
4426 rc = msi_publish_sourcelist(package, hukey);
4427 if (rc != ERROR_SUCCESS)
4428 goto end;
4430 rc = msi_publish_icons(package);
4432 end:
4433 uirow = MSI_CreateRecord( 1 );
4434 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4435 msi_ui_actiondata( package, szPublishProduct, uirow );
4436 msiobj_release( &uirow->hdr );
4438 RegCloseKey(hukey);
4439 RegCloseKey(hudkey);
4440 return rc;
4443 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4445 WCHAR *filename, *ptr, *folder, *ret;
4446 const WCHAR *dirprop;
4448 filename = msi_dup_record_field( row, 2 );
4449 if (filename && (ptr = strchrW( filename, '|' )))
4450 ptr++;
4451 else
4452 ptr = filename;
4454 dirprop = MSI_RecordGetString( row, 3 );
4455 if (dirprop)
4457 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4458 if (!folder) folder = msi_dup_property( package->db, dirprop );
4460 else
4461 folder = msi_dup_property( package->db, szWindowsFolder );
4463 if (!folder)
4465 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4466 msi_free( filename );
4467 return NULL;
4470 ret = msi_build_directory_name( 2, folder, ptr );
4472 msi_free( filename );
4473 msi_free( folder );
4474 return ret;
4477 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4479 MSIPACKAGE *package = param;
4480 LPCWSTR component, section, key, value, identifier;
4481 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4482 MSIRECORD * uirow;
4483 INT action;
4484 MSICOMPONENT *comp;
4486 component = MSI_RecordGetString(row, 8);
4487 comp = msi_get_loaded_component(package,component);
4488 if (!comp)
4489 return ERROR_SUCCESS;
4491 comp->Action = msi_get_component_action( package, comp );
4492 if (comp->Action != INSTALLSTATE_LOCAL)
4494 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4495 return ERROR_SUCCESS;
4498 identifier = MSI_RecordGetString(row,1);
4499 section = MSI_RecordGetString(row,4);
4500 key = MSI_RecordGetString(row,5);
4501 value = MSI_RecordGetString(row,6);
4502 action = MSI_RecordGetInteger(row,7);
4504 deformat_string(package,section,&deformated_section);
4505 deformat_string(package,key,&deformated_key);
4506 deformat_string(package,value,&deformated_value);
4508 fullname = get_ini_file_name(package, row);
4510 if (action == 0)
4512 TRACE("Adding value %s to section %s in %s\n",
4513 debugstr_w(deformated_key), debugstr_w(deformated_section),
4514 debugstr_w(fullname));
4515 WritePrivateProfileStringW(deformated_section, deformated_key,
4516 deformated_value, fullname);
4518 else if (action == 1)
4520 WCHAR returned[10];
4521 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4522 returned, 10, fullname);
4523 if (returned[0] == 0)
4525 TRACE("Adding value %s to section %s in %s\n",
4526 debugstr_w(deformated_key), debugstr_w(deformated_section),
4527 debugstr_w(fullname));
4529 WritePrivateProfileStringW(deformated_section, deformated_key,
4530 deformated_value, fullname);
4533 else if (action == 3)
4534 FIXME("Append to existing section not yet implemented\n");
4536 uirow = MSI_CreateRecord(4);
4537 MSI_RecordSetStringW(uirow,1,identifier);
4538 MSI_RecordSetStringW(uirow,2,deformated_section);
4539 MSI_RecordSetStringW(uirow,3,deformated_key);
4540 MSI_RecordSetStringW(uirow,4,deformated_value);
4541 msi_ui_actiondata( package, szWriteIniValues, uirow );
4542 msiobj_release( &uirow->hdr );
4544 msi_free(fullname);
4545 msi_free(deformated_key);
4546 msi_free(deformated_value);
4547 msi_free(deformated_section);
4548 return ERROR_SUCCESS;
4551 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4553 static const WCHAR query[] = {
4554 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4555 '`','I','n','i','F','i','l','e','`',0};
4556 MSIQUERY *view;
4557 UINT rc;
4559 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4560 if (rc != ERROR_SUCCESS)
4561 return ERROR_SUCCESS;
4563 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4564 msiobj_release(&view->hdr);
4565 return rc;
4568 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4570 MSIPACKAGE *package = param;
4571 LPCWSTR component, section, key, value, identifier;
4572 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4573 MSICOMPONENT *comp;
4574 MSIRECORD *uirow;
4575 INT action;
4577 component = MSI_RecordGetString( row, 8 );
4578 comp = msi_get_loaded_component( package, component );
4579 if (!comp)
4580 return ERROR_SUCCESS;
4582 comp->Action = msi_get_component_action( package, comp );
4583 if (comp->Action != INSTALLSTATE_ABSENT)
4585 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4586 return ERROR_SUCCESS;
4589 identifier = MSI_RecordGetString( row, 1 );
4590 section = MSI_RecordGetString( row, 4 );
4591 key = MSI_RecordGetString( row, 5 );
4592 value = MSI_RecordGetString( row, 6 );
4593 action = MSI_RecordGetInteger( row, 7 );
4595 deformat_string( package, section, &deformated_section );
4596 deformat_string( package, key, &deformated_key );
4597 deformat_string( package, value, &deformated_value );
4599 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4601 filename = get_ini_file_name( package, row );
4603 TRACE("Removing key %s from section %s in %s\n",
4604 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4606 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4608 WARN("Unable to remove key %u\n", GetLastError());
4610 msi_free( filename );
4612 else
4613 FIXME("Unsupported action %d\n", action);
4616 uirow = MSI_CreateRecord( 4 );
4617 MSI_RecordSetStringW( uirow, 1, identifier );
4618 MSI_RecordSetStringW( uirow, 2, deformated_section );
4619 MSI_RecordSetStringW( uirow, 3, deformated_key );
4620 MSI_RecordSetStringW( uirow, 4, deformated_value );
4621 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4622 msiobj_release( &uirow->hdr );
4624 msi_free( deformated_key );
4625 msi_free( deformated_value );
4626 msi_free( deformated_section );
4627 return ERROR_SUCCESS;
4630 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4632 MSIPACKAGE *package = param;
4633 LPCWSTR component, section, key, value, identifier;
4634 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4635 MSICOMPONENT *comp;
4636 MSIRECORD *uirow;
4637 INT action;
4639 component = MSI_RecordGetString( row, 8 );
4640 comp = msi_get_loaded_component( package, component );
4641 if (!comp)
4642 return ERROR_SUCCESS;
4644 comp->Action = msi_get_component_action( package, comp );
4645 if (comp->Action != INSTALLSTATE_LOCAL)
4647 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4648 return ERROR_SUCCESS;
4651 identifier = MSI_RecordGetString( row, 1 );
4652 section = MSI_RecordGetString( row, 4 );
4653 key = MSI_RecordGetString( row, 5 );
4654 value = MSI_RecordGetString( row, 6 );
4655 action = MSI_RecordGetInteger( row, 7 );
4657 deformat_string( package, section, &deformated_section );
4658 deformat_string( package, key, &deformated_key );
4659 deformat_string( package, value, &deformated_value );
4661 if (action == msidbIniFileActionRemoveLine)
4663 filename = get_ini_file_name( package, row );
4665 TRACE("Removing key %s from section %s in %s\n",
4666 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4668 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4670 WARN("Unable to remove key %u\n", GetLastError());
4672 msi_free( filename );
4674 else
4675 FIXME("Unsupported action %d\n", action);
4677 uirow = MSI_CreateRecord( 4 );
4678 MSI_RecordSetStringW( uirow, 1, identifier );
4679 MSI_RecordSetStringW( uirow, 2, deformated_section );
4680 MSI_RecordSetStringW( uirow, 3, deformated_key );
4681 MSI_RecordSetStringW( uirow, 4, deformated_value );
4682 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4683 msiobj_release( &uirow->hdr );
4685 msi_free( deformated_key );
4686 msi_free( deformated_value );
4687 msi_free( deformated_section );
4688 return ERROR_SUCCESS;
4691 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4693 static const WCHAR query[] = {
4694 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4695 '`','I','n','i','F','i','l','e','`',0};
4696 static const WCHAR remove_query[] = {
4697 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4698 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4699 MSIQUERY *view;
4700 UINT rc;
4702 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4703 if (rc == ERROR_SUCCESS)
4705 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4706 msiobj_release( &view->hdr );
4707 if (rc != ERROR_SUCCESS)
4708 return rc;
4710 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4711 if (rc == ERROR_SUCCESS)
4713 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4714 msiobj_release( &view->hdr );
4715 if (rc != ERROR_SUCCESS)
4716 return rc;
4718 return ERROR_SUCCESS;
4721 static void register_dll( const WCHAR *dll, BOOL unregister )
4723 static const WCHAR regW[] =
4724 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4725 static const WCHAR unregW[] =
4726 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4727 PROCESS_INFORMATION pi;
4728 STARTUPINFOW si;
4729 WCHAR *cmd;
4731 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4733 if (unregister) sprintfW( cmd, unregW, dll );
4734 else sprintfW( cmd, regW, dll );
4736 memset( &si, 0, sizeof(STARTUPINFOW) );
4737 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4739 CloseHandle( pi.hThread );
4740 msi_dialog_check_messages( pi.hProcess );
4741 CloseHandle( pi.hProcess );
4743 msi_free( cmd );
4746 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4748 MSIPACKAGE *package = param;
4749 LPCWSTR filename;
4750 MSIFILE *file;
4751 MSIRECORD *uirow;
4753 filename = MSI_RecordGetString( row, 1 );
4754 file = msi_get_loaded_file( package, filename );
4755 if (!file)
4757 WARN("unable to find file %s\n", debugstr_w(filename));
4758 return ERROR_SUCCESS;
4760 file->Component->Action = msi_get_component_action( package, file->Component );
4761 if (file->Component->Action != INSTALLSTATE_LOCAL)
4763 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4764 return ERROR_SUCCESS;
4767 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4768 register_dll( file->TargetPath, FALSE );
4770 uirow = MSI_CreateRecord( 2 );
4771 MSI_RecordSetStringW( uirow, 1, file->File );
4772 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4773 msi_ui_actiondata( package, szSelfRegModules, uirow );
4774 msiobj_release( &uirow->hdr );
4776 return ERROR_SUCCESS;
4779 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4781 static const WCHAR query[] = {
4782 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4783 '`','S','e','l','f','R','e','g','`',0};
4784 MSIQUERY *view;
4785 UINT rc;
4787 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4788 if (rc != ERROR_SUCCESS)
4789 return ERROR_SUCCESS;
4791 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4792 msiobj_release(&view->hdr);
4793 return rc;
4796 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4798 MSIPACKAGE *package = param;
4799 LPCWSTR filename;
4800 MSIFILE *file;
4801 MSIRECORD *uirow;
4803 filename = MSI_RecordGetString( row, 1 );
4804 file = msi_get_loaded_file( package, filename );
4805 if (!file)
4807 WARN("unable to find file %s\n", debugstr_w(filename));
4808 return ERROR_SUCCESS;
4810 file->Component->Action = msi_get_component_action( package, file->Component );
4811 if (file->Component->Action != INSTALLSTATE_ABSENT)
4813 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4814 return ERROR_SUCCESS;
4817 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4818 register_dll( file->TargetPath, TRUE );
4820 uirow = MSI_CreateRecord( 2 );
4821 MSI_RecordSetStringW( uirow, 1, file->File );
4822 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4823 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4824 msiobj_release( &uirow->hdr );
4826 return ERROR_SUCCESS;
4829 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4831 static const WCHAR query[] = {
4832 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4833 '`','S','e','l','f','R','e','g','`',0};
4834 MSIQUERY *view;
4835 UINT rc;
4837 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4838 if (rc != ERROR_SUCCESS)
4839 return ERROR_SUCCESS;
4841 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4842 msiobj_release( &view->hdr );
4843 return rc;
4846 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4848 MSIFEATURE *feature;
4849 UINT rc;
4850 HKEY hkey = NULL, userdata = NULL;
4852 if (!msi_check_publish(package))
4853 return ERROR_SUCCESS;
4855 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4856 &hkey, TRUE);
4857 if (rc != ERROR_SUCCESS)
4858 goto end;
4860 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4861 &userdata, TRUE);
4862 if (rc != ERROR_SUCCESS)
4863 goto end;
4865 /* here the guids are base 85 encoded */
4866 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4868 ComponentList *cl;
4869 LPWSTR data = NULL;
4870 GUID clsid;
4871 INT size;
4872 BOOL absent = FALSE;
4873 MSIRECORD *uirow;
4875 if (feature->Action != INSTALLSTATE_LOCAL &&
4876 feature->Action != INSTALLSTATE_SOURCE &&
4877 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4879 size = 1;
4880 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4882 size += 21;
4884 if (feature->Feature_Parent)
4885 size += strlenW( feature->Feature_Parent )+2;
4887 data = msi_alloc(size * sizeof(WCHAR));
4889 data[0] = 0;
4890 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4892 MSICOMPONENT* component = cl->component;
4893 WCHAR buf[21];
4895 buf[0] = 0;
4896 if (component->ComponentId)
4898 TRACE("From %s\n",debugstr_w(component->ComponentId));
4899 CLSIDFromString(component->ComponentId, &clsid);
4900 encode_base85_guid(&clsid,buf);
4901 TRACE("to %s\n",debugstr_w(buf));
4902 strcatW(data,buf);
4906 if (feature->Feature_Parent)
4908 static const WCHAR sep[] = {'\2',0};
4909 strcatW(data,sep);
4910 strcatW(data,feature->Feature_Parent);
4913 msi_reg_set_val_str( userdata, feature->Feature, data );
4914 msi_free(data);
4916 size = 0;
4917 if (feature->Feature_Parent)
4918 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4919 if (!absent)
4921 size += sizeof(WCHAR);
4922 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4923 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4925 else
4927 size += 2*sizeof(WCHAR);
4928 data = msi_alloc(size);
4929 data[0] = 0x6;
4930 data[1] = 0;
4931 if (feature->Feature_Parent)
4932 strcpyW( &data[1], feature->Feature_Parent );
4933 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4934 (LPBYTE)data,size);
4935 msi_free(data);
4938 /* the UI chunk */
4939 uirow = MSI_CreateRecord( 1 );
4940 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4941 msi_ui_actiondata( package, szPublishFeatures, uirow );
4942 msiobj_release( &uirow->hdr );
4943 /* FIXME: call msi_ui_progress? */
4946 end:
4947 RegCloseKey(hkey);
4948 RegCloseKey(userdata);
4949 return rc;
4952 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4954 UINT r;
4955 HKEY hkey;
4956 MSIRECORD *uirow;
4958 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4960 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4961 &hkey, FALSE);
4962 if (r == ERROR_SUCCESS)
4964 RegDeleteValueW(hkey, feature->Feature);
4965 RegCloseKey(hkey);
4968 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4969 &hkey, FALSE);
4970 if (r == ERROR_SUCCESS)
4972 RegDeleteValueW(hkey, feature->Feature);
4973 RegCloseKey(hkey);
4976 uirow = MSI_CreateRecord( 1 );
4977 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4978 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4979 msiobj_release( &uirow->hdr );
4981 return ERROR_SUCCESS;
4984 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4986 MSIFEATURE *feature;
4988 if (!msi_check_unpublish(package))
4989 return ERROR_SUCCESS;
4991 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4993 msi_unpublish_feature(package, feature);
4996 return ERROR_SUCCESS;
4999 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5001 SYSTEMTIME systime;
5002 DWORD size, langid;
5003 WCHAR date[9], *val, *buffer;
5004 const WCHAR *prop, *key;
5006 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5007 static const WCHAR modpath_fmt[] =
5008 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5009 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5010 static const WCHAR szModifyPath[] =
5011 {'M','o','d','i','f','y','P','a','t','h',0};
5012 static const WCHAR szUninstallString[] =
5013 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5014 static const WCHAR szEstimatedSize[] =
5015 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5016 static const WCHAR szDisplayVersion[] =
5017 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5018 static const WCHAR szInstallSource[] =
5019 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5020 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5021 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5022 static const WCHAR szAuthorizedCDFPrefix[] =
5023 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5024 static const WCHAR szARPCONTACT[] =
5025 {'A','R','P','C','O','N','T','A','C','T',0};
5026 static const WCHAR szContact[] =
5027 {'C','o','n','t','a','c','t',0};
5028 static const WCHAR szARPCOMMENTS[] =
5029 {'A','R','P','C','O','M','M','E','N','T','S',0};
5030 static const WCHAR szComments[] =
5031 {'C','o','m','m','e','n','t','s',0};
5032 static const WCHAR szProductName[] =
5033 {'P','r','o','d','u','c','t','N','a','m','e',0};
5034 static const WCHAR szDisplayName[] =
5035 {'D','i','s','p','l','a','y','N','a','m','e',0};
5036 static const WCHAR szARPHELPLINK[] =
5037 {'A','R','P','H','E','L','P','L','I','N','K',0};
5038 static const WCHAR szHelpLink[] =
5039 {'H','e','l','p','L','i','n','k',0};
5040 static const WCHAR szARPHELPTELEPHONE[] =
5041 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5042 static const WCHAR szHelpTelephone[] =
5043 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5044 static const WCHAR szARPINSTALLLOCATION[] =
5045 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5046 static const WCHAR szManufacturer[] =
5047 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5048 static const WCHAR szPublisher[] =
5049 {'P','u','b','l','i','s','h','e','r',0};
5050 static const WCHAR szARPREADME[] =
5051 {'A','R','P','R','E','A','D','M','E',0};
5052 static const WCHAR szReadme[] =
5053 {'R','e','a','d','M','e',0};
5054 static const WCHAR szARPSIZE[] =
5055 {'A','R','P','S','I','Z','E',0};
5056 static const WCHAR szSize[] =
5057 {'S','i','z','e',0};
5058 static const WCHAR szARPURLINFOABOUT[] =
5059 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5060 static const WCHAR szURLInfoAbout[] =
5061 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5062 static const WCHAR szARPURLUPDATEINFO[] =
5063 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5064 static const WCHAR szURLUpdateInfo[] =
5065 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5066 static const WCHAR szARPSYSTEMCOMPONENT[] =
5067 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5068 static const WCHAR szSystemComponent[] =
5069 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5071 static const WCHAR *propval[] = {
5072 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5073 szARPCONTACT, szContact,
5074 szARPCOMMENTS, szComments,
5075 szProductName, szDisplayName,
5076 szARPHELPLINK, szHelpLink,
5077 szARPHELPTELEPHONE, szHelpTelephone,
5078 szARPINSTALLLOCATION, szInstallLocation,
5079 szSourceDir, szInstallSource,
5080 szManufacturer, szPublisher,
5081 szARPREADME, szReadme,
5082 szARPSIZE, szSize,
5083 szARPURLINFOABOUT, szURLInfoAbout,
5084 szARPURLUPDATEINFO, szURLUpdateInfo,
5085 NULL
5087 const WCHAR **p = propval;
5089 while (*p)
5091 prop = *p++;
5092 key = *p++;
5093 val = msi_dup_property(package->db, prop);
5094 msi_reg_set_val_str(hkey, key, val);
5095 msi_free(val);
5098 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5099 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5101 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5103 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5104 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5105 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5106 msi_free(buffer);
5108 /* FIXME: Write real Estimated Size when we have it */
5109 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5111 GetLocalTime(&systime);
5112 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5113 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5115 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5116 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5118 buffer = msi_dup_property(package->db, szProductVersion);
5119 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5120 if (buffer)
5122 DWORD verdword = msi_version_str_to_dword(buffer);
5124 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5125 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5126 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5127 msi_free(buffer);
5130 return ERROR_SUCCESS;
5133 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5135 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5136 MSIRECORD *uirow;
5137 LPWSTR upgrade_code;
5138 HKEY hkey, props, upgrade_key;
5139 UINT rc;
5141 /* FIXME: also need to publish if the product is in advertise mode */
5142 if (!msi_check_publish(package))
5143 return ERROR_SUCCESS;
5145 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5146 if (rc != ERROR_SUCCESS)
5147 return rc;
5149 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5150 if (rc != ERROR_SUCCESS)
5151 goto done;
5153 rc = msi_publish_install_properties(package, hkey);
5154 if (rc != ERROR_SUCCESS)
5155 goto done;
5157 rc = msi_publish_install_properties(package, props);
5158 if (rc != ERROR_SUCCESS)
5159 goto done;
5161 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5162 if (upgrade_code)
5164 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5165 if (rc == ERROR_SUCCESS)
5167 squash_guid( package->ProductCode, squashed_pc );
5168 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5169 RegCloseKey( upgrade_key );
5171 msi_free( upgrade_code );
5173 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5174 package->delete_on_close = FALSE;
5176 done:
5177 uirow = MSI_CreateRecord( 1 );
5178 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5179 msi_ui_actiondata( package, szRegisterProduct, uirow );
5180 msiobj_release( &uirow->hdr );
5182 RegCloseKey(hkey);
5183 return ERROR_SUCCESS;
5186 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5188 return execute_script(package, SCRIPT_INSTALL);
5191 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5193 MSIPACKAGE *package = param;
5194 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5195 WCHAR *p, *icon_path;
5197 if (!icon) return ERROR_SUCCESS;
5198 if ((icon_path = msi_build_icon_path( package, icon )))
5200 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5201 DeleteFileW( icon_path );
5202 if ((p = strrchrW( icon_path, '\\' )))
5204 *p = 0;
5205 RemoveDirectoryW( icon_path );
5207 msi_free( icon_path );
5209 return ERROR_SUCCESS;
5212 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5214 static const WCHAR query[]= {
5215 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5216 MSIQUERY *view;
5217 UINT r;
5219 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5220 if (r == ERROR_SUCCESS)
5222 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5223 msiobj_release( &view->hdr );
5224 if (r != ERROR_SUCCESS)
5225 return r;
5227 return ERROR_SUCCESS;
5230 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5232 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5233 WCHAR *upgrade, **features;
5234 BOOL full_uninstall = TRUE;
5235 MSIFEATURE *feature;
5236 MSIPATCHINFO *patch;
5237 UINT i;
5239 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5241 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5243 features = msi_split_string( remove, ',' );
5244 for (i = 0; features && features[i]; i++)
5246 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5248 msi_free(features);
5250 if (!full_uninstall)
5251 return ERROR_SUCCESS;
5253 MSIREG_DeleteProductKey(package->ProductCode);
5254 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5255 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5257 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5258 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5259 MSIREG_DeleteUserProductKey(package->ProductCode);
5260 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5262 upgrade = msi_dup_property(package->db, szUpgradeCode);
5263 if (upgrade)
5265 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5266 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5267 msi_free(upgrade);
5270 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5272 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5273 if (!strcmpW( package->ProductCode, patch->products ))
5275 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5276 patch->delete_on_close = TRUE;
5278 /* FIXME: remove local patch package if this is the last product */
5280 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5281 package->delete_on_close = TRUE;
5283 msi_unpublish_icons( package );
5284 return ERROR_SUCCESS;
5287 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5289 UINT rc;
5290 WCHAR *remove;
5292 /* turn off scheduling */
5293 package->script->CurrentlyScripting= FALSE;
5295 /* first do the same as an InstallExecute */
5296 rc = ACTION_InstallExecute(package);
5297 if (rc != ERROR_SUCCESS)
5298 return rc;
5300 /* then handle commit actions */
5301 rc = execute_script(package, SCRIPT_COMMIT);
5302 if (rc != ERROR_SUCCESS)
5303 return rc;
5305 remove = msi_dup_property(package->db, szRemove);
5306 rc = msi_unpublish_product(package, remove);
5307 msi_free(remove);
5308 return rc;
5311 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5313 static const WCHAR RunOnce[] = {
5314 'S','o','f','t','w','a','r','e','\\',
5315 'M','i','c','r','o','s','o','f','t','\\',
5316 'W','i','n','d','o','w','s','\\',
5317 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5318 'R','u','n','O','n','c','e',0};
5319 static const WCHAR InstallRunOnce[] = {
5320 'S','o','f','t','w','a','r','e','\\',
5321 'M','i','c','r','o','s','o','f','t','\\',
5322 'W','i','n','d','o','w','s','\\',
5323 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5324 'I','n','s','t','a','l','l','e','r','\\',
5325 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5327 static const WCHAR msiexec_fmt[] = {
5328 '%','s',
5329 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5330 '\"','%','s','\"',0};
5331 static const WCHAR install_fmt[] = {
5332 '/','I',' ','\"','%','s','\"',' ',
5333 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5334 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5335 WCHAR buffer[256], sysdir[MAX_PATH];
5336 HKEY hkey;
5337 WCHAR squished_pc[100];
5339 squash_guid(package->ProductCode,squished_pc);
5341 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5342 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5343 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5344 squished_pc);
5346 msi_reg_set_val_str( hkey, squished_pc, buffer );
5347 RegCloseKey(hkey);
5349 TRACE("Reboot command %s\n",debugstr_w(buffer));
5351 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5352 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5354 msi_reg_set_val_str( hkey, squished_pc, buffer );
5355 RegCloseKey(hkey);
5357 return ERROR_INSTALL_SUSPEND;
5360 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5362 static const WCHAR query[] =
5363 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5364 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5365 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5366 MSIRECORD *rec, *row;
5367 DWORD i, size = 0;
5368 va_list va;
5369 const WCHAR *str;
5370 WCHAR *data;
5372 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5374 rec = MSI_CreateRecord( count + 2 );
5375 str = MSI_RecordGetString( row, 1 );
5376 MSI_RecordSetStringW( rec, 0, str );
5377 msiobj_release( &row->hdr );
5378 MSI_RecordSetInteger( rec, 1, error );
5380 va_start( va, count );
5381 for (i = 0; i < count; i++)
5383 str = va_arg( va, const WCHAR *);
5384 MSI_RecordSetStringW( rec, i + 2, str );
5386 va_end( va );
5388 MSI_FormatRecordW( package, rec, NULL, &size );
5389 size++;
5390 data = msi_alloc( size * sizeof(WCHAR) );
5391 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5392 else data[0] = 0;
5393 msiobj_release( &rec->hdr );
5394 return data;
5397 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5399 DWORD attrib;
5400 UINT rc;
5403 * We are currently doing what should be done here in the top level Install
5404 * however for Administrative and uninstalls this step will be needed
5406 if (!package->PackagePath)
5407 return ERROR_SUCCESS;
5409 msi_set_sourcedir_props(package, TRUE);
5411 attrib = GetFileAttributesW(package->db->path);
5412 if (attrib == INVALID_FILE_ATTRIBUTES)
5414 LPWSTR prompt, msg;
5415 DWORD size = 0;
5417 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5418 package->Context, MSICODE_PRODUCT,
5419 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5420 if (rc == ERROR_MORE_DATA)
5422 prompt = msi_alloc(size * sizeof(WCHAR));
5423 MsiSourceListGetInfoW(package->ProductCode, NULL,
5424 package->Context, MSICODE_PRODUCT,
5425 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5427 else
5428 prompt = strdupW(package->db->path);
5430 msg = msi_build_error_string(package, 1302, 1, prompt);
5431 msi_free(prompt);
5432 while(attrib == INVALID_FILE_ATTRIBUTES)
5434 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5435 if (rc == IDCANCEL)
5437 msi_free(msg);
5438 return ERROR_INSTALL_USEREXIT;
5440 attrib = GetFileAttributesW(package->db->path);
5442 msi_free(msg);
5443 rc = ERROR_SUCCESS;
5445 else
5446 return ERROR_SUCCESS;
5448 return rc;
5451 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5453 HKEY hkey = 0;
5454 LPWSTR buffer, productid = NULL;
5455 UINT i, rc = ERROR_SUCCESS;
5456 MSIRECORD *uirow;
5458 static const WCHAR szPropKeys[][80] =
5460 {'P','r','o','d','u','c','t','I','D',0},
5461 {'U','S','E','R','N','A','M','E',0},
5462 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5463 {0},
5466 static const WCHAR szRegKeys[][80] =
5468 {'P','r','o','d','u','c','t','I','D',0},
5469 {'R','e','g','O','w','n','e','r',0},
5470 {'R','e','g','C','o','m','p','a','n','y',0},
5471 {0},
5474 if (msi_check_unpublish(package))
5476 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5477 goto end;
5480 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5481 if (!productid)
5482 goto end;
5484 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5485 NULL, &hkey, TRUE);
5486 if (rc != ERROR_SUCCESS)
5487 goto end;
5489 for( i = 0; szPropKeys[i][0]; i++ )
5491 buffer = msi_dup_property( package->db, szPropKeys[i] );
5492 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5493 msi_free( buffer );
5496 end:
5497 uirow = MSI_CreateRecord( 1 );
5498 MSI_RecordSetStringW( uirow, 1, productid );
5499 msi_ui_actiondata( package, szRegisterUser, uirow );
5500 msiobj_release( &uirow->hdr );
5502 msi_free(productid);
5503 RegCloseKey(hkey);
5504 return rc;
5508 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5510 UINT rc;
5512 package->script->InWhatSequence |= SEQUENCE_EXEC;
5513 rc = ACTION_ProcessExecSequence(package,FALSE);
5514 return rc;
5517 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5519 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5520 WCHAR productid_85[21], component_85[21], *ret;
5521 GUID clsid;
5522 DWORD sz;
5524 /* > is used if there is a component GUID and < if not. */
5526 productid_85[0] = 0;
5527 component_85[0] = 0;
5528 CLSIDFromString( package->ProductCode, &clsid );
5530 encode_base85_guid( &clsid, productid_85 );
5531 if (component)
5533 CLSIDFromString( component->ComponentId, &clsid );
5534 encode_base85_guid( &clsid, component_85 );
5537 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5538 debugstr_w(component_85));
5540 sz = 20 + strlenW( feature ) + 20 + 3;
5541 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5542 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5543 return ret;
5546 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5548 MSIPACKAGE *package = param;
5549 LPCWSTR compgroupid, component, feature, qualifier, text;
5550 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5551 HKEY hkey = NULL;
5552 UINT rc;
5553 MSICOMPONENT *comp;
5554 MSIFEATURE *feat;
5555 DWORD sz;
5556 MSIRECORD *uirow;
5557 int len;
5559 feature = MSI_RecordGetString(rec, 5);
5560 feat = msi_get_loaded_feature(package, feature);
5561 if (!feat)
5562 return ERROR_SUCCESS;
5564 feat->Action = msi_get_feature_action( package, feat );
5565 if (feat->Action != INSTALLSTATE_LOCAL &&
5566 feat->Action != INSTALLSTATE_SOURCE &&
5567 feat->Action != INSTALLSTATE_ADVERTISED)
5569 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5570 return ERROR_SUCCESS;
5573 component = MSI_RecordGetString(rec, 3);
5574 comp = msi_get_loaded_component(package, component);
5575 if (!comp)
5576 return ERROR_SUCCESS;
5578 compgroupid = MSI_RecordGetString(rec,1);
5579 qualifier = MSI_RecordGetString(rec,2);
5581 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5582 if (rc != ERROR_SUCCESS)
5583 goto end;
5585 advertise = msi_create_component_advertise_string( package, comp, feature );
5586 text = MSI_RecordGetString( rec, 4 );
5587 if (text)
5589 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5590 strcpyW( p, advertise );
5591 strcatW( p, text );
5592 msi_free( advertise );
5593 advertise = p;
5595 existing = msi_reg_get_val_str( hkey, qualifier );
5597 sz = strlenW( advertise ) + 1;
5598 if (existing)
5600 for (p = existing; *p; p += len)
5602 len = strlenW( p ) + 1;
5603 if (strcmpW( advertise, p )) sz += len;
5606 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5608 rc = ERROR_OUTOFMEMORY;
5609 goto end;
5611 q = output;
5612 if (existing)
5614 for (p = existing; *p; p += len)
5616 len = strlenW( p ) + 1;
5617 if (strcmpW( advertise, p ))
5619 memcpy( q, p, len * sizeof(WCHAR) );
5620 q += len;
5624 strcpyW( q, advertise );
5625 q[strlenW( q ) + 1] = 0;
5627 msi_reg_set_val_multi_str( hkey, qualifier, output );
5629 end:
5630 RegCloseKey(hkey);
5631 msi_free( output );
5632 msi_free( advertise );
5633 msi_free( existing );
5635 /* the UI chunk */
5636 uirow = MSI_CreateRecord( 2 );
5637 MSI_RecordSetStringW( uirow, 1, compgroupid );
5638 MSI_RecordSetStringW( uirow, 2, qualifier);
5639 msi_ui_actiondata( package, szPublishComponents, uirow );
5640 msiobj_release( &uirow->hdr );
5641 /* FIXME: call ui_progress? */
5643 return rc;
5647 * At present I am ignorning the advertised components part of this and only
5648 * focusing on the qualified component sets
5650 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5652 static const WCHAR query[] = {
5653 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5654 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5655 MSIQUERY *view;
5656 UINT rc;
5658 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5659 if (rc != ERROR_SUCCESS)
5660 return ERROR_SUCCESS;
5662 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5663 msiobj_release(&view->hdr);
5664 return rc;
5667 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5669 static const WCHAR szInstallerComponents[] = {
5670 'S','o','f','t','w','a','r','e','\\',
5671 'M','i','c','r','o','s','o','f','t','\\',
5672 'I','n','s','t','a','l','l','e','r','\\',
5673 'C','o','m','p','o','n','e','n','t','s','\\',0};
5675 MSIPACKAGE *package = param;
5676 LPCWSTR compgroupid, component, feature, qualifier;
5677 MSICOMPONENT *comp;
5678 MSIFEATURE *feat;
5679 MSIRECORD *uirow;
5680 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5681 LONG res;
5683 feature = MSI_RecordGetString( rec, 5 );
5684 feat = msi_get_loaded_feature( package, feature );
5685 if (!feat)
5686 return ERROR_SUCCESS;
5688 feat->Action = msi_get_feature_action( package, feat );
5689 if (feat->Action != INSTALLSTATE_ABSENT)
5691 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5692 return ERROR_SUCCESS;
5695 component = MSI_RecordGetString( rec, 3 );
5696 comp = msi_get_loaded_component( package, component );
5697 if (!comp)
5698 return ERROR_SUCCESS;
5700 compgroupid = MSI_RecordGetString( rec, 1 );
5701 qualifier = MSI_RecordGetString( rec, 2 );
5703 squash_guid( compgroupid, squashed );
5704 strcpyW( keypath, szInstallerComponents );
5705 strcatW( keypath, squashed );
5707 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5708 if (res != ERROR_SUCCESS)
5710 WARN("Unable to delete component key %d\n", res);
5713 uirow = MSI_CreateRecord( 2 );
5714 MSI_RecordSetStringW( uirow, 1, compgroupid );
5715 MSI_RecordSetStringW( uirow, 2, qualifier );
5716 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5717 msiobj_release( &uirow->hdr );
5719 return ERROR_SUCCESS;
5722 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5724 static const WCHAR query[] = {
5725 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5726 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5727 MSIQUERY *view;
5728 UINT rc;
5730 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5731 if (rc != ERROR_SUCCESS)
5732 return ERROR_SUCCESS;
5734 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5735 msiobj_release( &view->hdr );
5736 return rc;
5739 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5741 static const WCHAR query[] =
5742 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5743 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5744 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5745 MSIPACKAGE *package = param;
5746 MSICOMPONENT *component;
5747 MSIRECORD *row;
5748 MSIFILE *file;
5749 SC_HANDLE hscm = NULL, service = NULL;
5750 LPCWSTR comp, key;
5751 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5752 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5753 DWORD serv_type, start_type, err_control;
5754 SERVICE_DESCRIPTIONW sd = {NULL};
5755 UINT ret = ERROR_SUCCESS;
5757 comp = MSI_RecordGetString( rec, 12 );
5758 component = msi_get_loaded_component( package, comp );
5759 if (!component)
5761 WARN("service component not found\n");
5762 goto done;
5764 component->Action = msi_get_component_action( package, component );
5765 if (component->Action != INSTALLSTATE_LOCAL)
5767 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5768 goto done;
5770 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5771 if (!hscm)
5773 ERR("Failed to open the SC Manager!\n");
5774 goto done;
5777 start_type = MSI_RecordGetInteger(rec, 5);
5778 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5779 goto done;
5781 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5782 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5783 serv_type = MSI_RecordGetInteger(rec, 4);
5784 err_control = MSI_RecordGetInteger(rec, 6);
5785 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5786 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5787 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5788 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5789 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5790 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5792 /* fetch the service path */
5793 row = MSI_QueryGetRecord(package->db, query, comp);
5794 if (!row)
5796 ERR("Query failed\n");
5797 goto done;
5799 key = MSI_RecordGetString(row, 6);
5800 file = msi_get_loaded_file(package, key);
5801 msiobj_release(&row->hdr);
5802 if (!file)
5804 ERR("Failed to load the service file\n");
5805 goto done;
5808 if (!args || !args[0]) image_path = file->TargetPath;
5809 else
5811 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5812 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5814 ret = ERROR_OUTOFMEMORY;
5815 goto done;
5818 strcpyW(image_path, file->TargetPath);
5819 strcatW(image_path, szSpace);
5820 strcatW(image_path, args);
5822 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5823 start_type, err_control, image_path, load_order,
5824 NULL, depends, serv_name, pass);
5826 if (!service)
5828 if (GetLastError() != ERROR_SERVICE_EXISTS)
5829 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5831 else if (sd.lpDescription)
5833 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5834 WARN("failed to set service description %u\n", GetLastError());
5837 if (image_path != file->TargetPath) msi_free(image_path);
5838 done:
5839 CloseServiceHandle(service);
5840 CloseServiceHandle(hscm);
5841 msi_free(name);
5842 msi_free(disp);
5843 msi_free(sd.lpDescription);
5844 msi_free(load_order);
5845 msi_free(serv_name);
5846 msi_free(pass);
5847 msi_free(depends);
5848 msi_free(args);
5850 return ret;
5853 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5855 static const WCHAR query[] = {
5856 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5857 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5858 MSIQUERY *view;
5859 UINT rc;
5861 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5862 if (rc != ERROR_SUCCESS)
5863 return ERROR_SUCCESS;
5865 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5866 msiobj_release(&view->hdr);
5867 return rc;
5870 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5871 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5873 LPCWSTR *vector, *temp_vector;
5874 LPWSTR p, q;
5875 DWORD sep_len;
5877 static const WCHAR separator[] = {'[','~',']',0};
5879 *numargs = 0;
5880 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5882 if (!args)
5883 return NULL;
5885 vector = msi_alloc(sizeof(LPWSTR));
5886 if (!vector)
5887 return NULL;
5889 p = args;
5892 (*numargs)++;
5893 vector[*numargs - 1] = p;
5895 if ((q = strstrW(p, separator)))
5897 *q = '\0';
5899 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5900 if (!temp_vector)
5902 msi_free(vector);
5903 return NULL;
5905 vector = temp_vector;
5907 p = q + sep_len;
5909 } while (q);
5911 return vector;
5914 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5916 MSIPACKAGE *package = param;
5917 MSICOMPONENT *comp;
5918 MSIRECORD *uirow;
5919 SC_HANDLE scm = NULL, service = NULL;
5920 LPCWSTR component, *vector = NULL;
5921 LPWSTR name, args, display_name = NULL;
5922 DWORD event, numargs, len, wait, dummy;
5923 UINT r = ERROR_FUNCTION_FAILED;
5924 SERVICE_STATUS_PROCESS status;
5925 ULONGLONG start_time;
5927 component = MSI_RecordGetString(rec, 6);
5928 comp = msi_get_loaded_component(package, component);
5929 if (!comp)
5930 return ERROR_SUCCESS;
5932 comp->Action = msi_get_component_action( package, comp );
5933 if (comp->Action != INSTALLSTATE_LOCAL)
5935 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5936 return ERROR_SUCCESS;
5939 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5940 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5941 event = MSI_RecordGetInteger(rec, 3);
5942 wait = MSI_RecordGetInteger(rec, 5);
5944 if (!(event & msidbServiceControlEventStart))
5946 r = ERROR_SUCCESS;
5947 goto done;
5950 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5951 if (!scm)
5953 ERR("Failed to open the service control manager\n");
5954 goto done;
5957 len = 0;
5958 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5959 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5961 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5962 GetServiceDisplayNameW( scm, name, display_name, &len );
5965 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5966 if (!service)
5968 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5969 goto done;
5972 vector = msi_service_args_to_vector(args, &numargs);
5974 if (!StartServiceW(service, numargs, vector) &&
5975 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5977 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5978 goto done;
5981 r = ERROR_SUCCESS;
5982 if (wait)
5984 /* wait for at most 30 seconds for the service to be up and running */
5985 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5986 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5988 TRACE("failed to query service status (%u)\n", GetLastError());
5989 goto done;
5991 start_time = GetTickCount64();
5992 while (status.dwCurrentState == SERVICE_START_PENDING)
5994 if (GetTickCount64() - start_time > 30000) break;
5995 Sleep(1000);
5996 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5997 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5999 TRACE("failed to query service status (%u)\n", GetLastError());
6000 goto done;
6003 if (status.dwCurrentState != SERVICE_RUNNING)
6005 WARN("service failed to start %u\n", status.dwCurrentState);
6006 r = ERROR_FUNCTION_FAILED;
6010 done:
6011 uirow = MSI_CreateRecord( 2 );
6012 MSI_RecordSetStringW( uirow, 1, display_name );
6013 MSI_RecordSetStringW( uirow, 2, name );
6014 msi_ui_actiondata( package, szStartServices, uirow );
6015 msiobj_release( &uirow->hdr );
6017 CloseServiceHandle(service);
6018 CloseServiceHandle(scm);
6020 msi_free(name);
6021 msi_free(args);
6022 msi_free(vector);
6023 msi_free(display_name);
6024 return r;
6027 static UINT ACTION_StartServices( MSIPACKAGE *package )
6029 static const WCHAR query[] = {
6030 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6031 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6032 MSIQUERY *view;
6033 UINT rc;
6035 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6036 if (rc != ERROR_SUCCESS)
6037 return ERROR_SUCCESS;
6039 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6040 msiobj_release(&view->hdr);
6041 return rc;
6044 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6046 DWORD i, needed, count;
6047 ENUM_SERVICE_STATUSW *dependencies;
6048 SERVICE_STATUS ss;
6049 SC_HANDLE depserv;
6050 BOOL stopped, ret = FALSE;
6052 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6053 0, &needed, &count))
6054 return TRUE;
6056 if (GetLastError() != ERROR_MORE_DATA)
6057 return FALSE;
6059 dependencies = msi_alloc(needed);
6060 if (!dependencies)
6061 return FALSE;
6063 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6064 needed, &needed, &count))
6065 goto done;
6067 for (i = 0; i < count; i++)
6069 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6070 SERVICE_STOP | SERVICE_QUERY_STATUS);
6071 if (!depserv)
6072 goto done;
6074 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6075 CloseServiceHandle(depserv);
6076 if (!stopped)
6077 goto done;
6080 ret = TRUE;
6082 done:
6083 msi_free(dependencies);
6084 return ret;
6087 static UINT stop_service( LPCWSTR name )
6089 SC_HANDLE scm = NULL, service = NULL;
6090 SERVICE_STATUS status;
6091 SERVICE_STATUS_PROCESS ssp;
6092 DWORD needed;
6094 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6095 if (!scm)
6097 WARN("Failed to open the SCM: %d\n", GetLastError());
6098 goto done;
6101 service = OpenServiceW(scm, name,
6102 SERVICE_STOP |
6103 SERVICE_QUERY_STATUS |
6104 SERVICE_ENUMERATE_DEPENDENTS);
6105 if (!service)
6107 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6108 goto done;
6111 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6112 sizeof(SERVICE_STATUS_PROCESS), &needed))
6114 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6115 goto done;
6118 if (ssp.dwCurrentState == SERVICE_STOPPED)
6119 goto done;
6121 stop_service_dependents(scm, service);
6123 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6124 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6126 done:
6127 CloseServiceHandle(service);
6128 CloseServiceHandle(scm);
6130 return ERROR_SUCCESS;
6133 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6135 MSIPACKAGE *package = param;
6136 MSICOMPONENT *comp;
6137 MSIRECORD *uirow;
6138 LPCWSTR component;
6139 LPWSTR name = NULL, display_name = NULL;
6140 DWORD event, len;
6141 SC_HANDLE scm;
6143 event = MSI_RecordGetInteger( rec, 3 );
6144 if (!(event & msidbServiceControlEventStop))
6145 return ERROR_SUCCESS;
6147 component = MSI_RecordGetString( rec, 6 );
6148 comp = msi_get_loaded_component( package, component );
6149 if (!comp)
6150 return ERROR_SUCCESS;
6152 comp->Action = msi_get_component_action( package, comp );
6153 if (comp->Action != INSTALLSTATE_ABSENT)
6155 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6156 return ERROR_SUCCESS;
6159 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6160 if (!scm)
6162 ERR("Failed to open the service control manager\n");
6163 goto done;
6166 len = 0;
6167 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6168 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6170 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6171 GetServiceDisplayNameW( scm, name, display_name, &len );
6173 CloseServiceHandle( scm );
6175 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6176 stop_service( name );
6178 done:
6179 uirow = MSI_CreateRecord( 2 );
6180 MSI_RecordSetStringW( uirow, 1, display_name );
6181 MSI_RecordSetStringW( uirow, 2, name );
6182 msi_ui_actiondata( package, szStopServices, uirow );
6183 msiobj_release( &uirow->hdr );
6185 msi_free( name );
6186 msi_free( display_name );
6187 return ERROR_SUCCESS;
6190 static UINT ACTION_StopServices( MSIPACKAGE *package )
6192 static const WCHAR query[] = {
6193 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6194 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6195 MSIQUERY *view;
6196 UINT rc;
6198 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6199 if (rc != ERROR_SUCCESS)
6200 return ERROR_SUCCESS;
6202 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6203 msiobj_release(&view->hdr);
6204 return rc;
6207 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6209 MSIPACKAGE *package = param;
6210 MSICOMPONENT *comp;
6211 MSIRECORD *uirow;
6212 LPWSTR name = NULL, display_name = NULL;
6213 DWORD event, len;
6214 SC_HANDLE scm = NULL, service = NULL;
6216 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6217 if (!comp)
6218 return ERROR_SUCCESS;
6220 event = MSI_RecordGetInteger( rec, 3 );
6221 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6223 comp->Action = msi_get_component_action( package, comp );
6224 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6225 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6227 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6228 msi_free( name );
6229 return ERROR_SUCCESS;
6231 stop_service( name );
6233 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6234 if (!scm)
6236 WARN("Failed to open the SCM: %d\n", GetLastError());
6237 goto done;
6240 len = 0;
6241 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6242 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6244 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6245 GetServiceDisplayNameW( scm, name, display_name, &len );
6248 service = OpenServiceW( scm, name, DELETE );
6249 if (!service)
6251 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6252 goto done;
6255 if (!DeleteService( service ))
6256 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6258 done:
6259 uirow = MSI_CreateRecord( 2 );
6260 MSI_RecordSetStringW( uirow, 1, display_name );
6261 MSI_RecordSetStringW( uirow, 2, name );
6262 msi_ui_actiondata( package, szDeleteServices, uirow );
6263 msiobj_release( &uirow->hdr );
6265 CloseServiceHandle( service );
6266 CloseServiceHandle( scm );
6267 msi_free( name );
6268 msi_free( display_name );
6270 return ERROR_SUCCESS;
6273 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6275 static const WCHAR query[] = {
6276 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6277 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6278 MSIQUERY *view;
6279 UINT rc;
6281 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6282 if (rc != ERROR_SUCCESS)
6283 return ERROR_SUCCESS;
6285 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6286 msiobj_release( &view->hdr );
6287 return rc;
6290 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6292 MSIPACKAGE *package = param;
6293 LPWSTR driver, driver_path, ptr;
6294 WCHAR outpath[MAX_PATH];
6295 MSIFILE *driver_file = NULL, *setup_file = NULL;
6296 MSICOMPONENT *comp;
6297 MSIRECORD *uirow;
6298 LPCWSTR desc, file_key, component;
6299 DWORD len, usage;
6300 UINT r = ERROR_SUCCESS;
6302 static const WCHAR driver_fmt[] = {
6303 'D','r','i','v','e','r','=','%','s',0};
6304 static const WCHAR setup_fmt[] = {
6305 'S','e','t','u','p','=','%','s',0};
6306 static const WCHAR usage_fmt[] = {
6307 'F','i','l','e','U','s','a','g','e','=','1',0};
6309 component = MSI_RecordGetString( rec, 2 );
6310 comp = msi_get_loaded_component( package, component );
6311 if (!comp)
6312 return ERROR_SUCCESS;
6314 comp->Action = msi_get_component_action( package, comp );
6315 if (comp->Action != INSTALLSTATE_LOCAL)
6317 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6318 return ERROR_SUCCESS;
6320 desc = MSI_RecordGetString(rec, 3);
6322 file_key = MSI_RecordGetString( rec, 4 );
6323 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6325 file_key = MSI_RecordGetString( rec, 5 );
6326 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6328 if (!driver_file)
6330 ERR("ODBC Driver entry not found!\n");
6331 return ERROR_FUNCTION_FAILED;
6334 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6335 if (setup_file)
6336 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6337 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6339 driver = msi_alloc(len * sizeof(WCHAR));
6340 if (!driver)
6341 return ERROR_OUTOFMEMORY;
6343 ptr = driver;
6344 lstrcpyW(ptr, desc);
6345 ptr += lstrlenW(ptr) + 1;
6347 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6348 ptr += len + 1;
6350 if (setup_file)
6352 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6353 ptr += len + 1;
6356 lstrcpyW(ptr, usage_fmt);
6357 ptr += lstrlenW(ptr) + 1;
6358 *ptr = '\0';
6360 if (!driver_file->TargetPath)
6362 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6363 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6365 driver_path = strdupW(driver_file->TargetPath);
6366 ptr = strrchrW(driver_path, '\\');
6367 if (ptr) *ptr = '\0';
6369 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6370 NULL, ODBC_INSTALL_COMPLETE, &usage))
6372 ERR("Failed to install SQL driver!\n");
6373 r = ERROR_FUNCTION_FAILED;
6376 uirow = MSI_CreateRecord( 5 );
6377 MSI_RecordSetStringW( uirow, 1, desc );
6378 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6379 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6380 msi_ui_actiondata( package, szInstallODBC, uirow );
6381 msiobj_release( &uirow->hdr );
6383 msi_free(driver);
6384 msi_free(driver_path);
6386 return r;
6389 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6391 MSIPACKAGE *package = param;
6392 LPWSTR translator, translator_path, ptr;
6393 WCHAR outpath[MAX_PATH];
6394 MSIFILE *translator_file = NULL, *setup_file = NULL;
6395 MSICOMPONENT *comp;
6396 MSIRECORD *uirow;
6397 LPCWSTR desc, file_key, component;
6398 DWORD len, usage;
6399 UINT r = ERROR_SUCCESS;
6401 static const WCHAR translator_fmt[] = {
6402 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6403 static const WCHAR setup_fmt[] = {
6404 'S','e','t','u','p','=','%','s',0};
6406 component = MSI_RecordGetString( rec, 2 );
6407 comp = msi_get_loaded_component( package, component );
6408 if (!comp)
6409 return ERROR_SUCCESS;
6411 comp->Action = msi_get_component_action( package, comp );
6412 if (comp->Action != INSTALLSTATE_LOCAL)
6414 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6415 return ERROR_SUCCESS;
6417 desc = MSI_RecordGetString(rec, 3);
6419 file_key = MSI_RecordGetString( rec, 4 );
6420 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6422 file_key = MSI_RecordGetString( rec, 5 );
6423 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6425 if (!translator_file)
6427 ERR("ODBC Translator entry not found!\n");
6428 return ERROR_FUNCTION_FAILED;
6431 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6432 if (setup_file)
6433 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6435 translator = msi_alloc(len * sizeof(WCHAR));
6436 if (!translator)
6437 return ERROR_OUTOFMEMORY;
6439 ptr = translator;
6440 lstrcpyW(ptr, desc);
6441 ptr += lstrlenW(ptr) + 1;
6443 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6444 ptr += len + 1;
6446 if (setup_file)
6448 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6449 ptr += len + 1;
6451 *ptr = '\0';
6453 translator_path = strdupW(translator_file->TargetPath);
6454 ptr = strrchrW(translator_path, '\\');
6455 if (ptr) *ptr = '\0';
6457 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6458 NULL, ODBC_INSTALL_COMPLETE, &usage))
6460 ERR("Failed to install SQL translator!\n");
6461 r = ERROR_FUNCTION_FAILED;
6464 uirow = MSI_CreateRecord( 5 );
6465 MSI_RecordSetStringW( uirow, 1, desc );
6466 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6467 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6468 msi_ui_actiondata( package, szInstallODBC, uirow );
6469 msiobj_release( &uirow->hdr );
6471 msi_free(translator);
6472 msi_free(translator_path);
6474 return r;
6477 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6479 MSIPACKAGE *package = param;
6480 MSICOMPONENT *comp;
6481 LPWSTR attrs;
6482 LPCWSTR desc, driver, component;
6483 WORD request = ODBC_ADD_SYS_DSN;
6484 INT registration;
6485 DWORD len;
6486 UINT r = ERROR_SUCCESS;
6487 MSIRECORD *uirow;
6489 static const WCHAR attrs_fmt[] = {
6490 'D','S','N','=','%','s',0 };
6492 component = MSI_RecordGetString( rec, 2 );
6493 comp = msi_get_loaded_component( package, component );
6494 if (!comp)
6495 return ERROR_SUCCESS;
6497 comp->Action = msi_get_component_action( package, comp );
6498 if (comp->Action != INSTALLSTATE_LOCAL)
6500 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6501 return ERROR_SUCCESS;
6504 desc = MSI_RecordGetString(rec, 3);
6505 driver = MSI_RecordGetString(rec, 4);
6506 registration = MSI_RecordGetInteger(rec, 5);
6508 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6509 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6511 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6512 attrs = msi_alloc(len * sizeof(WCHAR));
6513 if (!attrs)
6514 return ERROR_OUTOFMEMORY;
6516 len = sprintfW(attrs, attrs_fmt, desc);
6517 attrs[len + 1] = 0;
6519 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6521 ERR("Failed to install SQL data source!\n");
6522 r = ERROR_FUNCTION_FAILED;
6525 uirow = MSI_CreateRecord( 5 );
6526 MSI_RecordSetStringW( uirow, 1, desc );
6527 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6528 MSI_RecordSetInteger( uirow, 3, request );
6529 msi_ui_actiondata( package, szInstallODBC, uirow );
6530 msiobj_release( &uirow->hdr );
6532 msi_free(attrs);
6534 return r;
6537 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6539 static const WCHAR driver_query[] = {
6540 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6541 'O','D','B','C','D','r','i','v','e','r',0};
6542 static const WCHAR translator_query[] = {
6543 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6544 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6545 static const WCHAR source_query[] = {
6546 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6547 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6548 MSIQUERY *view;
6549 UINT rc;
6551 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6552 if (rc == ERROR_SUCCESS)
6554 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6555 msiobj_release(&view->hdr);
6556 if (rc != ERROR_SUCCESS)
6557 return rc;
6559 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6560 if (rc == ERROR_SUCCESS)
6562 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6563 msiobj_release(&view->hdr);
6564 if (rc != ERROR_SUCCESS)
6565 return rc;
6567 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6568 if (rc == ERROR_SUCCESS)
6570 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6571 msiobj_release(&view->hdr);
6572 if (rc != ERROR_SUCCESS)
6573 return rc;
6575 return ERROR_SUCCESS;
6578 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6580 MSIPACKAGE *package = param;
6581 MSICOMPONENT *comp;
6582 MSIRECORD *uirow;
6583 DWORD usage;
6584 LPCWSTR desc, component;
6586 component = MSI_RecordGetString( rec, 2 );
6587 comp = msi_get_loaded_component( package, component );
6588 if (!comp)
6589 return ERROR_SUCCESS;
6591 comp->Action = msi_get_component_action( package, comp );
6592 if (comp->Action != INSTALLSTATE_ABSENT)
6594 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6595 return ERROR_SUCCESS;
6598 desc = MSI_RecordGetString( rec, 3 );
6599 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6601 WARN("Failed to remove ODBC driver\n");
6603 else if (!usage)
6605 FIXME("Usage count reached 0\n");
6608 uirow = MSI_CreateRecord( 2 );
6609 MSI_RecordSetStringW( uirow, 1, desc );
6610 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6611 msi_ui_actiondata( package, szRemoveODBC, uirow );
6612 msiobj_release( &uirow->hdr );
6614 return ERROR_SUCCESS;
6617 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6619 MSIPACKAGE *package = param;
6620 MSICOMPONENT *comp;
6621 MSIRECORD *uirow;
6622 DWORD usage;
6623 LPCWSTR desc, component;
6625 component = MSI_RecordGetString( rec, 2 );
6626 comp = msi_get_loaded_component( package, component );
6627 if (!comp)
6628 return ERROR_SUCCESS;
6630 comp->Action = msi_get_component_action( package, comp );
6631 if (comp->Action != INSTALLSTATE_ABSENT)
6633 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6634 return ERROR_SUCCESS;
6637 desc = MSI_RecordGetString( rec, 3 );
6638 if (!SQLRemoveTranslatorW( desc, &usage ))
6640 WARN("Failed to remove ODBC translator\n");
6642 else if (!usage)
6644 FIXME("Usage count reached 0\n");
6647 uirow = MSI_CreateRecord( 2 );
6648 MSI_RecordSetStringW( uirow, 1, desc );
6649 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6650 msi_ui_actiondata( package, szRemoveODBC, uirow );
6651 msiobj_release( &uirow->hdr );
6653 return ERROR_SUCCESS;
6656 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6658 MSIPACKAGE *package = param;
6659 MSICOMPONENT *comp;
6660 MSIRECORD *uirow;
6661 LPWSTR attrs;
6662 LPCWSTR desc, driver, component;
6663 WORD request = ODBC_REMOVE_SYS_DSN;
6664 INT registration;
6665 DWORD len;
6667 static const WCHAR attrs_fmt[] = {
6668 'D','S','N','=','%','s',0 };
6670 component = MSI_RecordGetString( rec, 2 );
6671 comp = msi_get_loaded_component( package, component );
6672 if (!comp)
6673 return ERROR_SUCCESS;
6675 comp->Action = msi_get_component_action( package, comp );
6676 if (comp->Action != INSTALLSTATE_ABSENT)
6678 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6679 return ERROR_SUCCESS;
6682 desc = MSI_RecordGetString( rec, 3 );
6683 driver = MSI_RecordGetString( rec, 4 );
6684 registration = MSI_RecordGetInteger( rec, 5 );
6686 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6687 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6689 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6690 attrs = msi_alloc( len * sizeof(WCHAR) );
6691 if (!attrs)
6692 return ERROR_OUTOFMEMORY;
6694 FIXME("Use ODBCSourceAttribute table\n");
6696 len = sprintfW( attrs, attrs_fmt, desc );
6697 attrs[len + 1] = 0;
6699 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6701 WARN("Failed to remove ODBC data source\n");
6703 msi_free( attrs );
6705 uirow = MSI_CreateRecord( 3 );
6706 MSI_RecordSetStringW( uirow, 1, desc );
6707 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6708 MSI_RecordSetInteger( uirow, 3, request );
6709 msi_ui_actiondata( package, szRemoveODBC, uirow );
6710 msiobj_release( &uirow->hdr );
6712 return ERROR_SUCCESS;
6715 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6717 static const WCHAR driver_query[] = {
6718 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6719 'O','D','B','C','D','r','i','v','e','r',0};
6720 static const WCHAR translator_query[] = {
6721 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6722 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6723 static const WCHAR source_query[] = {
6724 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6725 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6726 MSIQUERY *view;
6727 UINT rc;
6729 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6730 if (rc == ERROR_SUCCESS)
6732 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6733 msiobj_release( &view->hdr );
6734 if (rc != ERROR_SUCCESS)
6735 return rc;
6737 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6738 if (rc == ERROR_SUCCESS)
6740 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6741 msiobj_release( &view->hdr );
6742 if (rc != ERROR_SUCCESS)
6743 return rc;
6745 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6746 if (rc == ERROR_SUCCESS)
6748 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6749 msiobj_release( &view->hdr );
6750 if (rc != ERROR_SUCCESS)
6751 return rc;
6753 return ERROR_SUCCESS;
6756 #define ENV_ACT_SETALWAYS 0x1
6757 #define ENV_ACT_SETABSENT 0x2
6758 #define ENV_ACT_REMOVE 0x4
6759 #define ENV_ACT_REMOVEMATCH 0x8
6761 #define ENV_MOD_MACHINE 0x20000000
6762 #define ENV_MOD_APPEND 0x40000000
6763 #define ENV_MOD_PREFIX 0x80000000
6764 #define ENV_MOD_MASK 0xC0000000
6766 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6768 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6770 LPCWSTR cptr = *name;
6772 static const WCHAR prefix[] = {'[','~',']',0};
6773 static const int prefix_len = 3;
6775 *flags = 0;
6776 while (*cptr)
6778 if (*cptr == '=')
6779 *flags |= ENV_ACT_SETALWAYS;
6780 else if (*cptr == '+')
6781 *flags |= ENV_ACT_SETABSENT;
6782 else if (*cptr == '-')
6783 *flags |= ENV_ACT_REMOVE;
6784 else if (*cptr == '!')
6785 *flags |= ENV_ACT_REMOVEMATCH;
6786 else if (*cptr == '*')
6787 *flags |= ENV_MOD_MACHINE;
6788 else
6789 break;
6791 cptr++;
6792 (*name)++;
6795 if (!*cptr)
6797 ERR("Missing environment variable\n");
6798 return ERROR_FUNCTION_FAILED;
6801 if (*value)
6803 LPCWSTR ptr = *value;
6804 if (!strncmpW(ptr, prefix, prefix_len))
6806 if (ptr[prefix_len] == szSemiColon[0])
6808 *flags |= ENV_MOD_APPEND;
6809 *value += lstrlenW(prefix);
6811 else
6813 *value = NULL;
6816 else if (lstrlenW(*value) >= prefix_len)
6818 ptr += lstrlenW(ptr) - prefix_len;
6819 if (!strcmpW( ptr, prefix ))
6821 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6823 *flags |= ENV_MOD_PREFIX;
6824 /* the "[~]" will be removed by deformat_string */;
6826 else
6828 *value = NULL;
6834 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6835 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6836 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6837 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6839 ERR("Invalid flags: %08x\n", *flags);
6840 return ERROR_FUNCTION_FAILED;
6843 if (!*flags)
6844 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6846 return ERROR_SUCCESS;
6849 static UINT open_env_key( DWORD flags, HKEY *key )
6851 static const WCHAR user_env[] =
6852 {'E','n','v','i','r','o','n','m','e','n','t',0};
6853 static const WCHAR machine_env[] =
6854 {'S','y','s','t','e','m','\\',
6855 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6856 'C','o','n','t','r','o','l','\\',
6857 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6858 'E','n','v','i','r','o','n','m','e','n','t',0};
6859 const WCHAR *env;
6860 HKEY root;
6861 LONG res;
6863 if (flags & ENV_MOD_MACHINE)
6865 env = machine_env;
6866 root = HKEY_LOCAL_MACHINE;
6868 else
6870 env = user_env;
6871 root = HKEY_CURRENT_USER;
6874 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6875 if (res != ERROR_SUCCESS)
6877 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6878 return ERROR_FUNCTION_FAILED;
6881 return ERROR_SUCCESS;
6884 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6886 MSIPACKAGE *package = param;
6887 LPCWSTR name, value, component;
6888 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6889 DWORD flags, type, size;
6890 UINT res;
6891 HKEY env = NULL;
6892 MSICOMPONENT *comp;
6893 MSIRECORD *uirow;
6894 int action = 0;
6896 component = MSI_RecordGetString(rec, 4);
6897 comp = msi_get_loaded_component(package, component);
6898 if (!comp)
6899 return ERROR_SUCCESS;
6901 comp->Action = msi_get_component_action( package, comp );
6902 if (comp->Action != INSTALLSTATE_LOCAL)
6904 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6905 return ERROR_SUCCESS;
6907 name = MSI_RecordGetString(rec, 2);
6908 value = MSI_RecordGetString(rec, 3);
6910 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6912 res = env_parse_flags(&name, &value, &flags);
6913 if (res != ERROR_SUCCESS || !value)
6914 goto done;
6916 if (value && !deformat_string(package, value, &deformatted))
6918 res = ERROR_OUTOFMEMORY;
6919 goto done;
6922 value = deformatted;
6924 res = open_env_key( flags, &env );
6925 if (res != ERROR_SUCCESS)
6926 goto done;
6928 if (flags & ENV_MOD_MACHINE)
6929 action |= 0x20000000;
6931 size = 0;
6932 type = REG_SZ;
6933 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6934 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6935 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6936 goto done;
6938 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6940 action = 0x2;
6942 /* Nothing to do. */
6943 if (!value)
6945 res = ERROR_SUCCESS;
6946 goto done;
6949 /* If we are appending but the string was empty, strip ; */
6950 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6952 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6953 newval = strdupW(value);
6954 if (!newval)
6956 res = ERROR_OUTOFMEMORY;
6957 goto done;
6960 else
6962 action = 0x1;
6964 /* Contrary to MSDN, +-variable to [~];path works */
6965 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6967 res = ERROR_SUCCESS;
6968 goto done;
6971 data = msi_alloc(size);
6972 if (!data)
6974 RegCloseKey(env);
6975 return ERROR_OUTOFMEMORY;
6978 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6979 if (res != ERROR_SUCCESS)
6980 goto done;
6982 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6984 action = 0x4;
6985 res = RegDeleteValueW(env, name);
6986 if (res != ERROR_SUCCESS)
6987 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6988 goto done;
6991 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6992 if (flags & ENV_MOD_MASK)
6994 DWORD mod_size;
6995 int multiplier = 0;
6996 if (flags & ENV_MOD_APPEND) multiplier++;
6997 if (flags & ENV_MOD_PREFIX) multiplier++;
6998 mod_size = lstrlenW(value) * multiplier;
6999 size += mod_size * sizeof(WCHAR);
7002 newval = msi_alloc(size);
7003 ptr = newval;
7004 if (!newval)
7006 res = ERROR_OUTOFMEMORY;
7007 goto done;
7010 if (flags & ENV_MOD_PREFIX)
7012 lstrcpyW(newval, value);
7013 ptr = newval + lstrlenW(value);
7014 action |= 0x80000000;
7017 lstrcpyW(ptr, data);
7019 if (flags & ENV_MOD_APPEND)
7021 lstrcatW(newval, value);
7022 action |= 0x40000000;
7025 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7026 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7027 if (res)
7029 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7032 done:
7033 uirow = MSI_CreateRecord( 3 );
7034 MSI_RecordSetStringW( uirow, 1, name );
7035 MSI_RecordSetStringW( uirow, 2, newval );
7036 MSI_RecordSetInteger( uirow, 3, action );
7037 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7038 msiobj_release( &uirow->hdr );
7040 if (env) RegCloseKey(env);
7041 msi_free(deformatted);
7042 msi_free(data);
7043 msi_free(newval);
7044 return res;
7047 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7049 static const WCHAR query[] = {
7050 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7051 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7052 MSIQUERY *view;
7053 UINT rc;
7055 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7056 if (rc != ERROR_SUCCESS)
7057 return ERROR_SUCCESS;
7059 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7060 msiobj_release(&view->hdr);
7061 return rc;
7064 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7066 MSIPACKAGE *package = param;
7067 LPCWSTR name, value, component;
7068 LPWSTR deformatted = NULL;
7069 DWORD flags;
7070 HKEY env;
7071 MSICOMPONENT *comp;
7072 MSIRECORD *uirow;
7073 int action = 0;
7074 LONG res;
7075 UINT r;
7077 component = MSI_RecordGetString( rec, 4 );
7078 comp = msi_get_loaded_component( package, component );
7079 if (!comp)
7080 return ERROR_SUCCESS;
7082 comp->Action = msi_get_component_action( package, comp );
7083 if (comp->Action != INSTALLSTATE_ABSENT)
7085 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7086 return ERROR_SUCCESS;
7088 name = MSI_RecordGetString( rec, 2 );
7089 value = MSI_RecordGetString( rec, 3 );
7091 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7093 r = env_parse_flags( &name, &value, &flags );
7094 if (r != ERROR_SUCCESS)
7095 return r;
7097 if (!(flags & ENV_ACT_REMOVE))
7099 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7100 return ERROR_SUCCESS;
7103 if (value && !deformat_string( package, value, &deformatted ))
7104 return ERROR_OUTOFMEMORY;
7106 value = deformatted;
7108 r = open_env_key( flags, &env );
7109 if (r != ERROR_SUCCESS)
7111 r = ERROR_SUCCESS;
7112 goto done;
7115 if (flags & ENV_MOD_MACHINE)
7116 action |= 0x20000000;
7118 TRACE("Removing %s\n", debugstr_w(name));
7120 res = RegDeleteValueW( env, name );
7121 if (res != ERROR_SUCCESS)
7123 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7124 r = ERROR_SUCCESS;
7127 done:
7128 uirow = MSI_CreateRecord( 3 );
7129 MSI_RecordSetStringW( uirow, 1, name );
7130 MSI_RecordSetStringW( uirow, 2, value );
7131 MSI_RecordSetInteger( uirow, 3, action );
7132 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7133 msiobj_release( &uirow->hdr );
7135 if (env) RegCloseKey( env );
7136 msi_free( deformatted );
7137 return r;
7140 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7142 static const WCHAR query[] = {
7143 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7144 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7145 MSIQUERY *view;
7146 UINT rc;
7148 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7149 if (rc != ERROR_SUCCESS)
7150 return ERROR_SUCCESS;
7152 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7153 msiobj_release( &view->hdr );
7154 return rc;
7157 UINT msi_validate_product_id( MSIPACKAGE *package )
7159 LPWSTR key, template, id;
7160 UINT r = ERROR_SUCCESS;
7162 id = msi_dup_property( package->db, szProductID );
7163 if (id)
7165 msi_free( id );
7166 return ERROR_SUCCESS;
7168 template = msi_dup_property( package->db, szPIDTemplate );
7169 key = msi_dup_property( package->db, szPIDKEY );
7170 if (key && template)
7172 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7173 r = msi_set_property( package->db, szProductID, key, -1 );
7175 msi_free( template );
7176 msi_free( key );
7177 return r;
7180 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7182 return msi_validate_product_id( package );
7185 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7187 TRACE("\n");
7188 package->need_reboot_at_end = 1;
7189 return ERROR_SUCCESS;
7192 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7194 static const WCHAR szAvailableFreeReg[] =
7195 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7196 MSIRECORD *uirow;
7197 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7199 TRACE("%p %d kilobytes\n", package, space);
7201 uirow = MSI_CreateRecord( 1 );
7202 MSI_RecordSetInteger( uirow, 1, space );
7203 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7204 msiobj_release( &uirow->hdr );
7206 return ERROR_SUCCESS;
7209 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7211 TRACE("%p\n", package);
7213 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7214 return ERROR_SUCCESS;
7217 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7219 FIXME("%p\n", package);
7220 return ERROR_SUCCESS;
7223 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7225 static const WCHAR driver_query[] = {
7226 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7227 'O','D','B','C','D','r','i','v','e','r',0};
7228 static const WCHAR translator_query[] = {
7229 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7230 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7231 MSIQUERY *view;
7232 UINT r, count;
7234 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7235 if (r == ERROR_SUCCESS)
7237 count = 0;
7238 r = MSI_IterateRecords( view, &count, NULL, package );
7239 msiobj_release( &view->hdr );
7240 if (r != ERROR_SUCCESS)
7241 return r;
7242 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7244 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7245 if (r == ERROR_SUCCESS)
7247 count = 0;
7248 r = MSI_IterateRecords( view, &count, NULL, package );
7249 msiobj_release( &view->hdr );
7250 if (r != ERROR_SUCCESS)
7251 return r;
7252 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7254 return ERROR_SUCCESS;
7257 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7259 static const WCHAR fmtW[] =
7260 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7261 MSIPACKAGE *package = param;
7262 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7263 int attrs = MSI_RecordGetInteger( rec, 5 );
7264 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7265 WCHAR *product, *features, *cmd;
7266 STARTUPINFOW si;
7267 PROCESS_INFORMATION info;
7268 BOOL ret;
7270 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7271 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7273 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7275 len += strlenW( product );
7276 if (features)
7277 len += strlenW( features );
7278 else
7279 len += sizeof(szAll) / sizeof(szAll[0]);
7281 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7283 msi_free( product );
7284 msi_free( features );
7285 return ERROR_OUTOFMEMORY;
7287 sprintfW( cmd, fmtW, product, features ? features : szAll );
7288 msi_free( product );
7289 msi_free( features );
7291 memset( &si, 0, sizeof(STARTUPINFOW) );
7292 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7293 msi_free( cmd );
7294 if (!ret) return GetLastError();
7295 CloseHandle( info.hThread );
7297 WaitForSingleObject( info.hProcess, INFINITE );
7298 CloseHandle( info.hProcess );
7299 return ERROR_SUCCESS;
7302 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7304 static const WCHAR query[] = {
7305 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7306 MSIQUERY *view;
7307 UINT r;
7309 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7310 if (r == ERROR_SUCCESS)
7312 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7313 msiobj_release( &view->hdr );
7314 if (r != ERROR_SUCCESS)
7315 return r;
7317 return ERROR_SUCCESS;
7320 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7322 MSIPACKAGE *package = param;
7323 int attributes = MSI_RecordGetInteger( rec, 5 );
7325 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7327 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7328 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7329 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7330 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7331 HKEY hkey;
7332 UINT r;
7334 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7336 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7337 if (r != ERROR_SUCCESS)
7338 return ERROR_SUCCESS;
7340 else
7342 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7343 if (r != ERROR_SUCCESS)
7344 return ERROR_SUCCESS;
7346 RegCloseKey( hkey );
7348 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7349 debugstr_w(upgrade_code), debugstr_w(version_min),
7350 debugstr_w(version_max), debugstr_w(language));
7352 return ERROR_SUCCESS;
7355 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7357 static const WCHAR query[] = {
7358 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7359 'U','p','g','r','a','d','e',0};
7360 MSIQUERY *view;
7361 UINT r;
7363 if (msi_get_property_int( package->db, szInstalled, 0 ))
7365 TRACE("product is installed, skipping action\n");
7366 return ERROR_SUCCESS;
7368 if (msi_get_property_int( package->db, szPreselected, 0 ))
7370 TRACE("Preselected property is set, not migrating feature states\n");
7371 return ERROR_SUCCESS;
7373 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7374 if (r == ERROR_SUCCESS)
7376 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7377 msiobj_release( &view->hdr );
7378 if (r != ERROR_SUCCESS)
7379 return r;
7381 return ERROR_SUCCESS;
7384 static void bind_image( const char *filename, const char *path )
7386 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7388 WARN("failed to bind image %u\n", GetLastError());
7392 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7394 UINT i;
7395 MSIFILE *file;
7396 MSIPACKAGE *package = param;
7397 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7398 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7399 char *filenameA, *pathA;
7400 WCHAR *pathW, **path_list;
7402 if (!(file = msi_get_loaded_file( package, key )))
7404 WARN("file %s not found\n", debugstr_w(key));
7405 return ERROR_SUCCESS;
7407 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7408 path_list = msi_split_string( paths, ';' );
7409 if (!path_list) bind_image( filenameA, NULL );
7410 else
7412 for (i = 0; path_list[i] && path_list[i][0]; i++)
7414 deformat_string( package, path_list[i], &pathW );
7415 if ((pathA = strdupWtoA( pathW )))
7417 bind_image( filenameA, pathA );
7418 msi_free( pathA );
7420 msi_free( pathW );
7423 msi_free( path_list );
7424 msi_free( filenameA );
7425 return ERROR_SUCCESS;
7428 static UINT ACTION_BindImage( MSIPACKAGE *package )
7430 static const WCHAR query[] = {
7431 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7432 'B','i','n','d','I','m','a','g','e',0};
7433 MSIQUERY *view;
7434 UINT r;
7436 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7437 if (r == ERROR_SUCCESS)
7439 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7440 msiobj_release( &view->hdr );
7441 if (r != ERROR_SUCCESS)
7442 return r;
7444 return ERROR_SUCCESS;
7447 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7449 static const WCHAR query[] = {
7450 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7451 MSIQUERY *view;
7452 DWORD count = 0;
7453 UINT r;
7455 r = MSI_OpenQuery( package->db, &view, query, table );
7456 if (r == ERROR_SUCCESS)
7458 r = MSI_IterateRecords(view, &count, NULL, package);
7459 msiobj_release(&view->hdr);
7460 if (r != ERROR_SUCCESS)
7461 return r;
7463 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7464 return ERROR_SUCCESS;
7467 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7469 static const WCHAR table[] = {
7470 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7471 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7474 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7476 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7477 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7480 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7482 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7483 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7486 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7488 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7489 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7492 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7494 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7495 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7498 static const struct
7500 const WCHAR *action;
7501 UINT (*handler)(MSIPACKAGE *);
7502 const WCHAR *action_rollback;
7504 StandardActions[] =
7506 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7507 { szAppSearch, ACTION_AppSearch, NULL },
7508 { szBindImage, ACTION_BindImage, NULL },
7509 { szCCPSearch, ACTION_CCPSearch, NULL },
7510 { szCostFinalize, ACTION_CostFinalize, NULL },
7511 { szCostInitialize, ACTION_CostInitialize, NULL },
7512 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7513 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7514 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7515 { szDisableRollback, ACTION_DisableRollback, NULL },
7516 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7517 { szExecuteAction, ACTION_ExecuteAction, NULL },
7518 { szFileCost, ACTION_FileCost, NULL },
7519 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7520 { szForceReboot, ACTION_ForceReboot, NULL },
7521 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7522 { szInstallExecute, ACTION_InstallExecute, NULL },
7523 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7524 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7525 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7526 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7527 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7528 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7529 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7530 { szInstallValidate, ACTION_InstallValidate, NULL },
7531 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7532 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7533 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7534 { szMoveFiles, ACTION_MoveFiles, NULL },
7535 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7536 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7537 { szPatchFiles, ACTION_PatchFiles, NULL },
7538 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7539 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7540 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7541 { szPublishProduct, ACTION_PublishProduct, NULL },
7542 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7543 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7544 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7545 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7546 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7547 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7548 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7549 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7550 { szRegisterUser, ACTION_RegisterUser, NULL },
7551 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7552 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7553 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7554 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7555 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7556 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7557 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7558 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7559 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7560 { szResolveSource, ACTION_ResolveSource, NULL },
7561 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7562 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7563 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7564 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7565 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7566 { szStartServices, ACTION_StartServices, szStopServices },
7567 { szStopServices, ACTION_StopServices, szStartServices },
7568 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7569 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7570 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7571 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7572 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7573 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7574 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7575 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7576 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7577 { szValidateProductID, ACTION_ValidateProductID, NULL },
7578 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7579 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7580 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7581 { NULL, NULL, NULL }
7584 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7586 BOOL ret = FALSE;
7587 UINT i;
7589 i = 0;
7590 while (StandardActions[i].action != NULL)
7592 if (!strcmpW( StandardActions[i].action, action ))
7594 ui_actionstart( package, action );
7595 if (StandardActions[i].handler)
7597 ui_actioninfo( package, action, TRUE, 0 );
7598 *rc = StandardActions[i].handler( package );
7599 ui_actioninfo( package, action, FALSE, *rc );
7601 if (StandardActions[i].action_rollback && !package->need_rollback)
7603 TRACE("scheduling rollback action\n");
7604 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7607 else
7609 FIXME("unhandled standard action %s\n", debugstr_w(action));
7610 *rc = ERROR_SUCCESS;
7612 ret = TRUE;
7613 break;
7615 i++;
7617 return ret;
7620 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7622 UINT rc = ERROR_SUCCESS;
7623 BOOL handled;
7625 TRACE("Performing action (%s)\n", debugstr_w(action));
7627 handled = ACTION_HandleStandardAction(package, action, &rc);
7629 if (!handled)
7630 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7632 if (!handled)
7634 WARN("unhandled msi action %s\n", debugstr_w(action));
7635 rc = ERROR_FUNCTION_NOT_CALLED;
7638 return rc;
7641 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7643 UINT rc = ERROR_SUCCESS;
7644 BOOL handled = FALSE;
7646 TRACE("Performing action (%s)\n", debugstr_w(action));
7648 package->action_progress_increment = 0;
7649 handled = ACTION_HandleStandardAction(package, action, &rc);
7651 if (!handled)
7652 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7654 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7655 handled = TRUE;
7657 if (!handled)
7659 WARN("unhandled msi action %s\n", debugstr_w(action));
7660 rc = ERROR_FUNCTION_NOT_CALLED;
7663 return rc;
7666 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7668 UINT rc = ERROR_SUCCESS;
7669 MSIRECORD *row;
7671 static const WCHAR query[] =
7672 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7673 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7674 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7675 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7676 static const WCHAR ui_query[] =
7677 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7678 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7679 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7680 ' ', '=',' ','%','i',0};
7682 if (needs_ui_sequence(package))
7683 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7684 else
7685 row = MSI_QueryGetRecord(package->db, query, seq);
7687 if (row)
7689 LPCWSTR action, cond;
7691 TRACE("Running the actions\n");
7693 /* check conditions */
7694 cond = MSI_RecordGetString(row, 2);
7696 /* this is a hack to skip errors in the condition code */
7697 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7699 msiobj_release(&row->hdr);
7700 return ERROR_SUCCESS;
7703 action = MSI_RecordGetString(row, 1);
7704 if (!action)
7706 ERR("failed to fetch action\n");
7707 msiobj_release(&row->hdr);
7708 return ERROR_FUNCTION_FAILED;
7711 if (needs_ui_sequence(package))
7712 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7713 else
7714 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7716 msiobj_release(&row->hdr);
7719 return rc;
7722 /****************************************************
7723 * TOP level entry points
7724 *****************************************************/
7726 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7727 LPCWSTR szCommandLine )
7729 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7730 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7731 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7732 WCHAR *reinstall, *remove, *patch;
7733 BOOL ui_exists;
7734 UINT rc;
7736 msi_set_property( package->db, szAction, szInstall, -1 );
7738 package->script->InWhatSequence = SEQUENCE_INSTALL;
7740 if (szPackagePath)
7742 LPWSTR p, dir;
7743 LPCWSTR file;
7745 dir = strdupW(szPackagePath);
7746 p = strrchrW(dir, '\\');
7747 if (p)
7749 *(++p) = 0;
7750 file = szPackagePath + (p - dir);
7752 else
7754 msi_free(dir);
7755 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7756 GetCurrentDirectoryW(MAX_PATH, dir);
7757 lstrcatW(dir, szBackSlash);
7758 file = szPackagePath;
7761 msi_free( package->PackagePath );
7762 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7763 if (!package->PackagePath)
7765 msi_free(dir);
7766 return ERROR_OUTOFMEMORY;
7769 lstrcpyW(package->PackagePath, dir);
7770 lstrcatW(package->PackagePath, file);
7771 msi_free(dir);
7773 msi_set_sourcedir_props(package, FALSE);
7776 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7777 if (rc != ERROR_SUCCESS)
7778 return rc;
7780 msi_apply_transforms( package );
7781 msi_apply_patches( package );
7783 patch = msi_dup_property( package->db, szPatch );
7784 remove = msi_dup_property( package->db, szRemove );
7785 reinstall = msi_dup_property( package->db, szReinstall );
7786 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7788 TRACE("setting REINSTALL property to ALL\n");
7789 msi_set_property( package->db, szReinstall, szAll, -1 );
7790 package->full_reinstall = 1;
7793 /* properties may have been added by a transform */
7794 msi_clone_properties( package );
7795 msi_set_original_database_property( package->db, szPackagePath );
7797 msi_parse_command_line( package, szCommandLine, FALSE );
7798 msi_adjust_privilege_properties( package );
7799 msi_set_context( package );
7801 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7803 TRACE("disabling rollback\n");
7804 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7807 if (needs_ui_sequence( package))
7809 package->script->InWhatSequence |= SEQUENCE_UI;
7810 rc = ACTION_ProcessUISequence(package);
7811 ui_exists = ui_sequence_exists(package);
7812 if (rc == ERROR_SUCCESS || !ui_exists)
7814 package->script->InWhatSequence |= SEQUENCE_EXEC;
7815 rc = ACTION_ProcessExecSequence(package, ui_exists);
7818 else
7819 rc = ACTION_ProcessExecSequence(package, FALSE);
7821 package->script->CurrentlyScripting = FALSE;
7823 /* process the ending type action */
7824 if (rc == ERROR_SUCCESS)
7825 ACTION_PerformActionSequence(package, -1);
7826 else if (rc == ERROR_INSTALL_USEREXIT)
7827 ACTION_PerformActionSequence(package, -2);
7828 else if (rc == ERROR_INSTALL_SUSPEND)
7829 ACTION_PerformActionSequence(package, -4);
7830 else /* failed */
7832 ACTION_PerformActionSequence(package, -3);
7833 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7835 package->need_rollback = TRUE;
7839 /* finish up running custom actions */
7840 ACTION_FinishCustomActions(package);
7842 if (package->need_rollback && !reinstall)
7844 WARN("installation failed, running rollback script\n");
7845 execute_script( package, SCRIPT_ROLLBACK );
7847 msi_free( reinstall );
7848 msi_free( remove );
7849 msi_free( patch );
7851 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7852 return ERROR_SUCCESS_REBOOT_REQUIRED;
7854 return rc;