msi: Ignore the value if a registry value name is special.
[wine/multimedia.git] / dlls / msi / action.c
blob5ba7ec3ad192f92bd616ac8df4585f7612a5d7e8
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 );
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 );
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 );
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);
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 return comp->ActionRequest;
815 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
817 if (package->need_rollback) return feature->Installed;
818 return feature->ActionRequest;
821 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
823 MSIPACKAGE *package = param;
824 LPCWSTR dir, component, full_path;
825 MSIRECORD *uirow;
826 MSIFOLDER *folder;
827 MSICOMPONENT *comp;
829 component = MSI_RecordGetString(row, 2);
830 if (!component)
831 return ERROR_SUCCESS;
833 comp = msi_get_loaded_component(package, component);
834 if (!comp)
835 return ERROR_SUCCESS;
837 comp->Action = msi_get_component_action( package, comp );
838 if (comp->Action != INSTALLSTATE_LOCAL)
840 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
841 return ERROR_SUCCESS;
844 dir = MSI_RecordGetString(row,1);
845 if (!dir)
847 ERR("Unable to get folder id\n");
848 return ERROR_SUCCESS;
851 uirow = MSI_CreateRecord(1);
852 MSI_RecordSetStringW(uirow, 1, dir);
853 msi_ui_actiondata(package, szCreateFolders, uirow);
854 msiobj_release(&uirow->hdr);
856 full_path = msi_get_target_folder( package, dir );
857 if (!full_path)
859 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
860 return ERROR_SUCCESS;
862 TRACE("folder is %s\n", debugstr_w(full_path));
864 folder = msi_get_loaded_folder( package, dir );
865 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
866 folder->State = FOLDER_STATE_CREATED;
867 return ERROR_SUCCESS;
870 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
872 static const WCHAR query[] = {
873 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
874 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
875 MSIQUERY *view;
876 UINT rc;
878 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
879 if (rc != ERROR_SUCCESS)
880 return ERROR_SUCCESS;
882 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
883 msiobj_release(&view->hdr);
884 return rc;
887 static void remove_persistent_folder( MSIFOLDER *folder )
889 FolderList *fl;
891 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
893 remove_persistent_folder( fl->folder );
895 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
897 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
901 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
903 MSIPACKAGE *package = param;
904 LPCWSTR dir, component, full_path;
905 MSIRECORD *uirow;
906 MSIFOLDER *folder;
907 MSICOMPONENT *comp;
909 component = MSI_RecordGetString(row, 2);
910 if (!component)
911 return ERROR_SUCCESS;
913 comp = msi_get_loaded_component(package, component);
914 if (!comp)
915 return ERROR_SUCCESS;
917 comp->Action = msi_get_component_action( package, comp );
918 if (comp->Action != INSTALLSTATE_ABSENT)
920 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
921 return ERROR_SUCCESS;
924 dir = MSI_RecordGetString( row, 1 );
925 if (!dir)
927 ERR("Unable to get folder id\n");
928 return ERROR_SUCCESS;
931 full_path = msi_get_target_folder( package, dir );
932 if (!full_path)
934 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
935 return ERROR_SUCCESS;
937 TRACE("folder is %s\n", debugstr_w(full_path));
939 uirow = MSI_CreateRecord( 1 );
940 MSI_RecordSetStringW( uirow, 1, dir );
941 msi_ui_actiondata( package, szRemoveFolders, uirow );
942 msiobj_release( &uirow->hdr );
944 folder = msi_get_loaded_folder( package, dir );
945 remove_persistent_folder( folder );
946 return ERROR_SUCCESS;
949 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
951 static const WCHAR query[] = {
952 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
953 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
954 MSIQUERY *view;
955 UINT rc;
957 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
958 if (rc != ERROR_SUCCESS)
959 return ERROR_SUCCESS;
961 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
962 msiobj_release( &view->hdr );
963 return rc;
966 static UINT load_component( MSIRECORD *row, LPVOID param )
968 MSIPACKAGE *package = param;
969 MSICOMPONENT *comp;
971 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
972 if (!comp)
973 return ERROR_FUNCTION_FAILED;
975 list_add_tail( &package->components, &comp->entry );
977 /* fill in the data */
978 comp->Component = msi_dup_record_field( row, 1 );
980 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
982 comp->ComponentId = msi_dup_record_field( row, 2 );
983 comp->Directory = msi_dup_record_field( row, 3 );
984 comp->Attributes = MSI_RecordGetInteger(row,4);
985 comp->Condition = msi_dup_record_field( row, 5 );
986 comp->KeyPath = msi_dup_record_field( row, 6 );
988 comp->Installed = INSTALLSTATE_UNKNOWN;
989 comp->Action = INSTALLSTATE_UNKNOWN;
990 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
992 comp->assembly = msi_load_assembly( package, comp );
993 return ERROR_SUCCESS;
996 UINT msi_load_all_components( MSIPACKAGE *package )
998 static const WCHAR query[] = {
999 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1000 '`','C','o','m','p','o','n','e','n','t','`',0};
1001 MSIQUERY *view;
1002 UINT r;
1004 if (!list_empty(&package->components))
1005 return ERROR_SUCCESS;
1007 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1008 if (r != ERROR_SUCCESS)
1009 return r;
1011 if (!msi_init_assembly_caches( package ))
1013 ERR("can't initialize assembly caches\n");
1014 msiobj_release( &view->hdr );
1015 return ERROR_FUNCTION_FAILED;
1018 r = MSI_IterateRecords(view, NULL, load_component, package);
1019 msiobj_release(&view->hdr);
1020 msi_destroy_assembly_caches( package );
1021 return r;
1024 typedef struct {
1025 MSIPACKAGE *package;
1026 MSIFEATURE *feature;
1027 } _ilfs;
1029 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1031 ComponentList *cl;
1033 cl = msi_alloc( sizeof (*cl) );
1034 if ( !cl )
1035 return ERROR_NOT_ENOUGH_MEMORY;
1036 cl->component = comp;
1037 list_add_tail( &feature->Components, &cl->entry );
1039 return ERROR_SUCCESS;
1042 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1044 FeatureList *fl;
1046 fl = msi_alloc( sizeof(*fl) );
1047 if ( !fl )
1048 return ERROR_NOT_ENOUGH_MEMORY;
1049 fl->feature = child;
1050 list_add_tail( &parent->Children, &fl->entry );
1052 return ERROR_SUCCESS;
1055 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1057 _ilfs* ilfs = param;
1058 LPCWSTR component;
1059 MSICOMPONENT *comp;
1061 component = MSI_RecordGetString(row,1);
1063 /* check to see if the component is already loaded */
1064 comp = msi_get_loaded_component( ilfs->package, component );
1065 if (!comp)
1067 WARN("ignoring unknown component %s\n", debugstr_w(component));
1068 return ERROR_SUCCESS;
1070 add_feature_component( ilfs->feature, comp );
1071 comp->Enabled = TRUE;
1073 return ERROR_SUCCESS;
1076 static UINT load_feature(MSIRECORD * row, LPVOID param)
1078 static const WCHAR query[] = {
1079 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1080 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1081 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1082 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1083 MSIPACKAGE *package = param;
1084 MSIFEATURE *feature;
1085 MSIQUERY *view;
1086 _ilfs ilfs;
1087 UINT rc;
1089 /* fill in the data */
1091 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1092 if (!feature)
1093 return ERROR_NOT_ENOUGH_MEMORY;
1095 list_init( &feature->Children );
1096 list_init( &feature->Components );
1098 feature->Feature = msi_dup_record_field( row, 1 );
1100 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1102 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1103 feature->Title = msi_dup_record_field( row, 3 );
1104 feature->Description = msi_dup_record_field( row, 4 );
1106 if (!MSI_RecordIsNull(row,5))
1107 feature->Display = MSI_RecordGetInteger(row,5);
1109 feature->Level= MSI_RecordGetInteger(row,6);
1110 feature->Directory = msi_dup_record_field( row, 7 );
1111 feature->Attributes = MSI_RecordGetInteger(row,8);
1113 feature->Installed = INSTALLSTATE_UNKNOWN;
1114 feature->Action = INSTALLSTATE_UNKNOWN;
1115 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1117 list_add_tail( &package->features, &feature->entry );
1119 /* load feature components */
1121 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1122 if (rc != ERROR_SUCCESS)
1123 return ERROR_SUCCESS;
1125 ilfs.package = package;
1126 ilfs.feature = feature;
1128 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1129 msiobj_release(&view->hdr);
1130 return rc;
1133 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1135 MSIPACKAGE *package = param;
1136 MSIFEATURE *parent, *child;
1138 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1139 if (!child)
1140 return ERROR_FUNCTION_FAILED;
1142 if (!child->Feature_Parent)
1143 return ERROR_SUCCESS;
1145 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1146 if (!parent)
1147 return ERROR_FUNCTION_FAILED;
1149 add_feature_child( parent, child );
1150 return ERROR_SUCCESS;
1153 UINT msi_load_all_features( MSIPACKAGE *package )
1155 static const WCHAR query[] = {
1156 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1157 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1158 '`','D','i','s','p','l','a','y','`',0};
1159 MSIQUERY *view;
1160 UINT r;
1162 if (!list_empty(&package->features))
1163 return ERROR_SUCCESS;
1165 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1166 if (r != ERROR_SUCCESS)
1167 return r;
1169 r = MSI_IterateRecords( view, NULL, load_feature, package );
1170 if (r != ERROR_SUCCESS)
1172 msiobj_release( &view->hdr );
1173 return r;
1175 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1176 msiobj_release( &view->hdr );
1177 return r;
1180 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1182 if (!p)
1183 return p;
1184 p = strchrW(p, ch);
1185 if (!p)
1186 return p;
1187 *p = 0;
1188 return p+1;
1191 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1193 static const WCHAR query[] = {
1194 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1195 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1196 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1197 MSIQUERY *view = NULL;
1198 MSIRECORD *row = NULL;
1199 UINT r;
1201 TRACE("%s\n", debugstr_w(file->File));
1203 r = MSI_OpenQuery(package->db, &view, query, file->File);
1204 if (r != ERROR_SUCCESS)
1205 goto done;
1207 r = MSI_ViewExecute(view, NULL);
1208 if (r != ERROR_SUCCESS)
1209 goto done;
1211 r = MSI_ViewFetch(view, &row);
1212 if (r != ERROR_SUCCESS)
1213 goto done;
1215 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1216 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1217 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1218 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1219 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1221 done:
1222 if (view) msiobj_release(&view->hdr);
1223 if (row) msiobj_release(&row->hdr);
1224 return r;
1227 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1229 MSIRECORD *row;
1230 static const WCHAR query[] = {
1231 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1232 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1233 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1235 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1236 if (!row)
1238 WARN("query failed\n");
1239 return ERROR_FUNCTION_FAILED;
1242 file->disk_id = MSI_RecordGetInteger( row, 1 );
1243 msiobj_release( &row->hdr );
1244 return ERROR_SUCCESS;
1247 static UINT load_file(MSIRECORD *row, LPVOID param)
1249 MSIPACKAGE* package = param;
1250 LPCWSTR component;
1251 MSIFILE *file;
1253 /* fill in the data */
1255 file = msi_alloc_zero( sizeof (MSIFILE) );
1256 if (!file)
1257 return ERROR_NOT_ENOUGH_MEMORY;
1259 file->File = msi_dup_record_field( row, 1 );
1261 component = MSI_RecordGetString( row, 2 );
1262 file->Component = msi_get_loaded_component( package, component );
1264 if (!file->Component)
1266 WARN("Component not found: %s\n", debugstr_w(component));
1267 msi_free(file->File);
1268 msi_free(file);
1269 return ERROR_SUCCESS;
1272 file->FileName = msi_dup_record_field( row, 3 );
1273 msi_reduce_to_long_filename( file->FileName );
1275 file->ShortName = msi_dup_record_field( row, 3 );
1276 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1278 file->FileSize = MSI_RecordGetInteger( row, 4 );
1279 file->Version = msi_dup_record_field( row, 5 );
1280 file->Language = msi_dup_record_field( row, 6 );
1281 file->Attributes = MSI_RecordGetInteger( row, 7 );
1282 file->Sequence = MSI_RecordGetInteger( row, 8 );
1284 file->state = msifs_invalid;
1286 /* if the compressed bits are not set in the file attributes,
1287 * then read the information from the package word count property
1289 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1291 file->IsCompressed = FALSE;
1293 else if (file->Attributes &
1294 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1296 file->IsCompressed = TRUE;
1298 else if (file->Attributes & msidbFileAttributesNoncompressed)
1300 file->IsCompressed = FALSE;
1302 else
1304 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1307 load_file_hash(package, file);
1308 load_file_disk_id(package, file);
1310 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1312 list_add_tail( &package->files, &file->entry );
1314 return ERROR_SUCCESS;
1317 static UINT load_all_files(MSIPACKAGE *package)
1319 static const WCHAR query[] = {
1320 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1321 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1322 '`','S','e','q','u','e','n','c','e','`', 0};
1323 MSIQUERY *view;
1324 UINT rc;
1326 if (!list_empty(&package->files))
1327 return ERROR_SUCCESS;
1329 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1330 if (rc != ERROR_SUCCESS)
1331 return ERROR_SUCCESS;
1333 rc = MSI_IterateRecords(view, NULL, load_file, package);
1334 msiobj_release(&view->hdr);
1335 return rc;
1338 static UINT load_media( MSIRECORD *row, LPVOID param )
1340 MSIPACKAGE *package = param;
1341 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1342 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1344 /* FIXME: load external cabinets and directory sources too */
1345 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1346 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1347 return ERROR_SUCCESS;
1350 static UINT load_all_media( MSIPACKAGE *package )
1352 static const WCHAR query[] = {
1353 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1354 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1355 '`','D','i','s','k','I','d','`',0};
1356 MSIQUERY *view;
1357 UINT r;
1359 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1360 if (r != ERROR_SUCCESS)
1361 return ERROR_SUCCESS;
1363 r = MSI_IterateRecords( view, NULL, load_media, package );
1364 msiobj_release( &view->hdr );
1365 return r;
1368 static UINT load_patch(MSIRECORD *row, LPVOID param)
1370 MSIPACKAGE *package = param;
1371 MSIFILEPATCH *patch;
1372 LPWSTR file_key;
1374 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1375 if (!patch)
1376 return ERROR_NOT_ENOUGH_MEMORY;
1378 file_key = msi_dup_record_field( row, 1 );
1379 patch->File = msi_get_loaded_file( package, file_key );
1380 msi_free(file_key);
1382 if( !patch->File )
1384 ERR("Failed to find target for patch in File table\n");
1385 msi_free(patch);
1386 return ERROR_FUNCTION_FAILED;
1389 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1391 /* FIXME: The database should be properly transformed */
1392 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1394 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1395 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1396 patch->IsApplied = FALSE;
1398 /* FIXME:
1399 * Header field - for patch validation.
1400 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1403 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1405 list_add_tail( &package->filepatches, &patch->entry );
1407 return ERROR_SUCCESS;
1410 static UINT load_all_patches(MSIPACKAGE *package)
1412 static const WCHAR query[] = {
1413 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1414 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1415 '`','S','e','q','u','e','n','c','e','`',0};
1416 MSIQUERY *view;
1417 UINT rc;
1419 if (!list_empty(&package->filepatches))
1420 return ERROR_SUCCESS;
1422 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1423 if (rc != ERROR_SUCCESS)
1424 return ERROR_SUCCESS;
1426 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1427 msiobj_release(&view->hdr);
1428 return rc;
1431 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1433 static const WCHAR query[] = {
1434 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1435 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1436 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1437 MSIQUERY *view;
1439 folder->persistent = FALSE;
1440 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1442 if (!MSI_ViewExecute( view, NULL ))
1444 MSIRECORD *rec;
1445 if (!MSI_ViewFetch( view, &rec ))
1447 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1448 folder->persistent = TRUE;
1449 msiobj_release( &rec->hdr );
1452 msiobj_release( &view->hdr );
1454 return ERROR_SUCCESS;
1457 static UINT load_folder( MSIRECORD *row, LPVOID param )
1459 MSIPACKAGE *package = param;
1460 static WCHAR szEmpty[] = { 0 };
1461 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1462 MSIFOLDER *folder;
1464 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1465 list_init( &folder->children );
1466 folder->Directory = msi_dup_record_field( row, 1 );
1467 folder->Parent = msi_dup_record_field( row, 2 );
1468 p = msi_dup_record_field(row, 3);
1470 TRACE("%s\n", debugstr_w(folder->Directory));
1472 /* split src and target dir */
1473 tgt_short = p;
1474 src_short = folder_split_path( p, ':' );
1476 /* split the long and short paths */
1477 tgt_long = folder_split_path( tgt_short, '|' );
1478 src_long = folder_split_path( src_short, '|' );
1480 /* check for no-op dirs */
1481 if (tgt_short && !strcmpW( szDot, tgt_short ))
1482 tgt_short = szEmpty;
1483 if (src_short && !strcmpW( szDot, src_short ))
1484 src_short = szEmpty;
1486 if (!tgt_long)
1487 tgt_long = tgt_short;
1489 if (!src_short) {
1490 src_short = tgt_short;
1491 src_long = tgt_long;
1494 if (!src_long)
1495 src_long = src_short;
1497 /* FIXME: use the target short path too */
1498 folder->TargetDefault = strdupW(tgt_long);
1499 folder->SourceShortPath = strdupW(src_short);
1500 folder->SourceLongPath = strdupW(src_long);
1501 msi_free(p);
1503 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1504 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1505 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1507 load_folder_persistence( package, folder );
1509 list_add_tail( &package->folders, &folder->entry );
1510 return ERROR_SUCCESS;
1513 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1515 FolderList *fl;
1517 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1518 fl->folder = child;
1519 list_add_tail( &parent->children, &fl->entry );
1520 return ERROR_SUCCESS;
1523 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1525 MSIPACKAGE *package = param;
1526 MSIFOLDER *parent, *child;
1528 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1529 return ERROR_FUNCTION_FAILED;
1531 if (!child->Parent) return ERROR_SUCCESS;
1533 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1534 return ERROR_FUNCTION_FAILED;
1536 return add_folder_child( parent, child );
1539 static UINT load_all_folders( MSIPACKAGE *package )
1541 static const WCHAR query[] = {
1542 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1543 '`','D','i','r','e','c','t','o','r','y','`',0};
1544 MSIQUERY *view;
1545 UINT r;
1547 if (!list_empty(&package->folders))
1548 return ERROR_SUCCESS;
1550 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1551 if (r != ERROR_SUCCESS)
1552 return r;
1554 r = MSI_IterateRecords( view, NULL, load_folder, package );
1555 if (r != ERROR_SUCCESS)
1557 msiobj_release( &view->hdr );
1558 return r;
1560 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1561 msiobj_release( &view->hdr );
1562 return r;
1565 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1567 msi_set_property( package->db, szCostingComplete, szZero );
1568 msi_set_property( package->db, szRootDrive, szCRoot );
1570 load_all_folders( package );
1571 msi_load_all_components( package );
1572 msi_load_all_features( package );
1573 load_all_files( package );
1574 load_all_patches( package );
1575 load_all_media( package );
1577 return ERROR_SUCCESS;
1580 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1582 const WCHAR *action = package->script->Actions[script][index];
1583 ui_actionstart( package, action );
1584 TRACE("executing %s\n", debugstr_w(action));
1585 return ACTION_PerformAction( package, action, script );
1588 static UINT execute_script( MSIPACKAGE *package, UINT script )
1590 UINT i, rc = ERROR_SUCCESS;
1592 TRACE("executing script %u\n", script);
1594 if (!package->script)
1596 ERR("no script!\n");
1597 return ERROR_FUNCTION_FAILED;
1599 if (script == SCRIPT_ROLLBACK)
1601 for (i = package->script->ActionCount[script]; i > 0; i--)
1603 rc = execute_script_action( package, script, i - 1 );
1604 if (rc != ERROR_SUCCESS) break;
1607 else
1609 for (i = 0; i < package->script->ActionCount[script]; i++)
1611 rc = execute_script_action( package, script, i );
1612 if (rc != ERROR_SUCCESS) break;
1615 msi_free_action_script(package, script);
1616 return rc;
1619 static UINT ACTION_FileCost(MSIPACKAGE *package)
1621 return ERROR_SUCCESS;
1624 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1626 MSICOMPONENT *comp;
1627 UINT r;
1629 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1631 if (!comp->ComponentId) continue;
1633 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1634 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1635 &comp->Installed );
1636 if (r == ERROR_SUCCESS) continue;
1638 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1639 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1640 &comp->Installed );
1641 if (r == ERROR_SUCCESS) continue;
1643 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1644 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1645 &comp->Installed );
1646 if (r == ERROR_SUCCESS) continue;
1648 comp->Installed = INSTALLSTATE_ABSENT;
1652 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1654 MSIFEATURE *feature;
1656 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1658 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1660 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1661 feature->Installed = INSTALLSTATE_ABSENT;
1662 else
1663 feature->Installed = state;
1667 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1669 return (feature->Level > 0 && feature->Level <= level);
1672 static BOOL process_state_property(MSIPACKAGE* package, int level,
1673 LPCWSTR property, INSTALLSTATE state)
1675 LPWSTR override;
1676 MSIFEATURE *feature;
1678 override = msi_dup_property( package->db, property );
1679 if (!override)
1680 return FALSE;
1682 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1684 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1685 continue;
1687 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1689 if (!strcmpiW( override, szAll ))
1691 if (feature->Installed != state)
1693 feature->Action = state;
1694 feature->ActionRequest = state;
1697 else
1699 LPWSTR ptr = override;
1700 LPWSTR ptr2 = strchrW(override,',');
1702 while (ptr)
1704 int len = ptr2 - ptr;
1706 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1707 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1709 if (feature->Installed != state)
1711 feature->Action = state;
1712 feature->ActionRequest = state;
1714 break;
1716 if (ptr2)
1718 ptr=ptr2+1;
1719 ptr2 = strchrW(ptr,',');
1721 else
1722 break;
1726 msi_free(override);
1727 return TRUE;
1730 static BOOL process_overrides( MSIPACKAGE *package, int level )
1732 static const WCHAR szAddLocal[] =
1733 {'A','D','D','L','O','C','A','L',0};
1734 static const WCHAR szAddSource[] =
1735 {'A','D','D','S','O','U','R','C','E',0};
1736 static const WCHAR szAdvertise[] =
1737 {'A','D','V','E','R','T','I','S','E',0};
1738 BOOL ret = FALSE;
1740 /* all these activation/deactivation things happen in order and things
1741 * later on the list override things earlier on the list.
1743 * 0 INSTALLLEVEL processing
1744 * 1 ADDLOCAL
1745 * 2 REMOVE
1746 * 3 ADDSOURCE
1747 * 4 ADDDEFAULT
1748 * 5 REINSTALL
1749 * 6 ADVERTISE
1750 * 7 COMPADDLOCAL
1751 * 8 COMPADDSOURCE
1752 * 9 FILEADDLOCAL
1753 * 10 FILEADDSOURCE
1754 * 11 FILEADDDEFAULT
1756 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1757 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1758 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1759 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1760 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1762 if (ret)
1763 msi_set_property( package->db, szPreselected, szOne );
1765 return ret;
1768 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1770 int level;
1771 MSICOMPONENT* component;
1772 MSIFEATURE *feature;
1774 TRACE("Checking Install Level\n");
1776 level = msi_get_property_int(package->db, szInstallLevel, 1);
1778 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1780 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1782 if (!is_feature_selected( feature, level )) continue;
1784 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1786 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1788 feature->Action = INSTALLSTATE_SOURCE;
1789 feature->ActionRequest = INSTALLSTATE_SOURCE;
1791 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1793 feature->Action = INSTALLSTATE_ADVERTISED;
1794 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1796 else
1798 feature->Action = INSTALLSTATE_LOCAL;
1799 feature->ActionRequest = INSTALLSTATE_LOCAL;
1803 /* disable child features of unselected parent or follow parent */
1804 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1806 FeatureList *fl;
1808 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1810 if (!is_feature_selected( feature, level ))
1812 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1813 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1815 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1817 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1818 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1819 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1820 fl->feature->Action = feature->Action;
1821 fl->feature->ActionRequest = feature->ActionRequest;
1826 else /* preselected */
1828 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1830 if (!is_feature_selected( feature, level )) continue;
1832 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1834 if (feature->Installed == INSTALLSTATE_ABSENT)
1836 feature->Action = INSTALLSTATE_UNKNOWN;
1837 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1839 else
1841 feature->Action = feature->Installed;
1842 feature->ActionRequest = feature->Installed;
1846 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1848 FeatureList *fl;
1850 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1852 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1853 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1855 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1856 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1857 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1858 fl->feature->Action = feature->Action;
1859 fl->feature->ActionRequest = feature->ActionRequest;
1865 /* now we want to set component state based based on feature state */
1866 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1868 ComponentList *cl;
1870 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1871 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1872 feature->ActionRequest, feature->Action);
1874 if (!is_feature_selected( feature, level )) continue;
1876 /* features with components that have compressed files are made local */
1877 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1879 if (cl->component->ForceLocalState &&
1880 feature->ActionRequest == INSTALLSTATE_SOURCE)
1882 feature->Action = INSTALLSTATE_LOCAL;
1883 feature->ActionRequest = INSTALLSTATE_LOCAL;
1884 break;
1888 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1890 component = cl->component;
1892 switch (feature->ActionRequest)
1894 case INSTALLSTATE_ABSENT:
1895 component->anyAbsent = 1;
1896 break;
1897 case INSTALLSTATE_ADVERTISED:
1898 component->hasAdvertiseFeature = 1;
1899 break;
1900 case INSTALLSTATE_SOURCE:
1901 component->hasSourceFeature = 1;
1902 break;
1903 case INSTALLSTATE_LOCAL:
1904 component->hasLocalFeature = 1;
1905 break;
1906 case INSTALLSTATE_DEFAULT:
1907 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1908 component->hasAdvertiseFeature = 1;
1909 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1910 component->hasSourceFeature = 1;
1911 else
1912 component->hasLocalFeature = 1;
1913 break;
1914 default:
1915 break;
1920 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1922 /* check if it's local or source */
1923 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1924 (component->hasLocalFeature || component->hasSourceFeature))
1926 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1927 !component->ForceLocalState)
1929 component->Action = INSTALLSTATE_SOURCE;
1930 component->ActionRequest = INSTALLSTATE_SOURCE;
1932 else
1934 component->Action = INSTALLSTATE_LOCAL;
1935 component->ActionRequest = INSTALLSTATE_LOCAL;
1937 continue;
1940 /* if any feature is local, the component must be local too */
1941 if (component->hasLocalFeature)
1943 component->Action = INSTALLSTATE_LOCAL;
1944 component->ActionRequest = INSTALLSTATE_LOCAL;
1945 continue;
1947 if (component->hasSourceFeature)
1949 component->Action = INSTALLSTATE_SOURCE;
1950 component->ActionRequest = INSTALLSTATE_SOURCE;
1951 continue;
1953 if (component->hasAdvertiseFeature)
1955 component->Action = INSTALLSTATE_ADVERTISED;
1956 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1957 continue;
1959 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1960 if (component->anyAbsent &&
1961 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1963 component->Action = INSTALLSTATE_ABSENT;
1964 component->ActionRequest = INSTALLSTATE_ABSENT;
1968 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1970 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1972 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1973 component->Action = INSTALLSTATE_LOCAL;
1974 component->ActionRequest = INSTALLSTATE_LOCAL;
1977 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1978 component->Installed == INSTALLSTATE_SOURCE &&
1979 component->hasSourceFeature)
1981 component->Action = INSTALLSTATE_UNKNOWN;
1982 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1985 TRACE("component %s (installed %d request %d action %d)\n",
1986 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1989 return ERROR_SUCCESS;
1992 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1994 MSIPACKAGE *package = param;
1995 LPCWSTR name;
1996 MSIFEATURE *feature;
1998 name = MSI_RecordGetString( row, 1 );
2000 feature = msi_get_loaded_feature( package, name );
2001 if (!feature)
2002 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2003 else
2005 LPCWSTR Condition;
2006 Condition = MSI_RecordGetString(row,3);
2008 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2010 int level = MSI_RecordGetInteger(row,2);
2011 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2012 feature->Level = level;
2015 return ERROR_SUCCESS;
2018 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2020 static const WCHAR name[] = {'\\',0};
2021 VS_FIXEDFILEINFO *ptr, *ret;
2022 LPVOID version;
2023 DWORD versize, handle;
2024 UINT sz;
2026 versize = GetFileVersionInfoSizeW( filename, &handle );
2027 if (!versize)
2028 return NULL;
2030 version = msi_alloc( versize );
2031 if (!version)
2032 return NULL;
2034 GetFileVersionInfoW( filename, 0, versize, version );
2036 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2038 msi_free( version );
2039 return NULL;
2042 ret = msi_alloc( sz );
2043 memcpy( ret, ptr, sz );
2045 msi_free( version );
2046 return ret;
2049 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2051 DWORD ms, ls;
2053 msi_parse_version_string( version, &ms, &ls );
2055 if (fi->dwFileVersionMS > ms) return 1;
2056 else if (fi->dwFileVersionMS < ms) return -1;
2057 else if (fi->dwFileVersionLS > ls) return 1;
2058 else if (fi->dwFileVersionLS < ls) return -1;
2059 return 0;
2062 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2064 DWORD ms1, ms2;
2066 msi_parse_version_string( ver1, &ms1, NULL );
2067 msi_parse_version_string( ver2, &ms2, NULL );
2069 if (ms1 > ms2) return 1;
2070 else if (ms1 < ms2) return -1;
2071 return 0;
2074 DWORD msi_get_disk_file_size( LPCWSTR filename )
2076 HANDLE file;
2077 DWORD size;
2079 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2080 if (file == INVALID_HANDLE_VALUE)
2081 return INVALID_FILE_SIZE;
2083 size = GetFileSize( file, NULL );
2084 TRACE("size is %u\n", size);
2085 CloseHandle( file );
2086 return size;
2089 BOOL msi_file_hash_matches( MSIFILE *file )
2091 UINT r;
2092 MSIFILEHASHINFO hash;
2094 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2095 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2096 if (r != ERROR_SUCCESS)
2097 return FALSE;
2099 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2102 static WCHAR *get_temp_dir( void )
2104 static UINT id;
2105 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2107 GetTempPathW( MAX_PATH, tmp );
2108 for (;;)
2110 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2111 if (CreateDirectoryW( dir, NULL )) break;
2113 return strdupW( dir );
2117 * msi_build_directory_name()
2119 * This function is to save messing round with directory names
2120 * It handles adding backslashes between path segments,
2121 * and can add \ at the end of the directory name if told to.
2123 * It takes a variable number of arguments.
2124 * It always allocates a new string for the result, so make sure
2125 * to free the return value when finished with it.
2127 * The first arg is the number of path segments that follow.
2128 * The arguments following count are a list of path segments.
2129 * A path segment may be NULL.
2131 * Path segments will be added with a \ separating them.
2132 * A \ will not be added after the last segment, however if the
2133 * last segment is NULL, then the last character will be a \
2135 WCHAR *msi_build_directory_name( DWORD count, ... )
2137 DWORD sz = 1, i;
2138 WCHAR *dir;
2139 va_list va;
2141 va_start( va, count );
2142 for (i = 0; i < count; i++)
2144 const WCHAR *str = va_arg( va, const WCHAR * );
2145 if (str) sz += strlenW( str ) + 1;
2147 va_end( va );
2149 dir = msi_alloc( sz * sizeof(WCHAR) );
2150 dir[0] = 0;
2152 va_start( va, count );
2153 for (i = 0; i < count; i++)
2155 const WCHAR *str = va_arg( va, const WCHAR * );
2156 if (!str) continue;
2157 strcatW( dir, str );
2158 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2160 va_end( va );
2161 return dir;
2164 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2166 MSIASSEMBLY *assembly = file->Component->assembly;
2168 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2170 msi_free( file->TargetPath );
2171 if (assembly && !assembly->application)
2173 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2174 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2175 msi_track_tempfile( package, file->TargetPath );
2177 else
2179 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2180 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2183 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2186 static UINT calculate_file_cost( MSIPACKAGE *package )
2188 VS_FIXEDFILEINFO *file_version;
2189 WCHAR *font_version;
2190 MSIFILE *file;
2192 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2194 MSICOMPONENT *comp = file->Component;
2195 DWORD file_size;
2197 if (!comp->Enabled) continue;
2199 if (file->IsCompressed)
2200 comp->ForceLocalState = TRUE;
2202 set_target_path( package, file );
2204 if ((comp->assembly && !comp->assembly->installed) ||
2205 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2207 comp->Cost += file->FileSize;
2208 continue;
2210 file_size = msi_get_disk_file_size( file->TargetPath );
2212 if (file->Version)
2214 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2216 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2218 comp->Cost += file->FileSize - file_size;
2220 msi_free( file_version );
2221 continue;
2223 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2225 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2227 comp->Cost += file->FileSize - file_size;
2229 msi_free( font_version );
2230 continue;
2233 if (file_size != file->FileSize)
2235 comp->Cost += file->FileSize - file_size;
2238 return ERROR_SUCCESS;
2241 WCHAR *msi_normalize_path( const WCHAR *in )
2243 const WCHAR *p = in;
2244 WCHAR *q, *ret;
2245 int n, len = strlenW( in ) + 2;
2247 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2249 len = 0;
2250 while (1)
2252 /* copy until the end of the string or a space */
2253 while (*p != ' ' && (*q = *p))
2255 p++, len++;
2256 /* reduce many backslashes to one */
2257 if (*p != '\\' || *q != '\\')
2258 q++;
2261 /* quit at the end of the string */
2262 if (!*p)
2263 break;
2265 /* count the number of spaces */
2266 n = 0;
2267 while (p[n] == ' ')
2268 n++;
2270 /* if it's leading or trailing space, skip it */
2271 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2272 p += n;
2273 else /* copy n spaces */
2274 while (n && (*q++ = *p++)) n--;
2276 while (q - ret > 0 && q[-1] == ' ') q--;
2277 if (q - ret > 0 && q[-1] != '\\')
2279 q[0] = '\\';
2280 q[1] = 0;
2282 return ret;
2285 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2287 FolderList *fl;
2288 MSIFOLDER *folder, *parent, *child;
2289 WCHAR *path, *normalized_path;
2291 TRACE("resolving %s\n", debugstr_w(name));
2293 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2295 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2297 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
2299 path = msi_dup_property( package->db, szRootDrive );
2302 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2304 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2306 parent = msi_get_loaded_folder( package, folder->Parent );
2307 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2309 else
2310 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2312 normalized_path = msi_normalize_path( path );
2313 msi_free( path );
2314 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2316 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2317 msi_free( normalized_path );
2318 return;
2320 msi_set_property( package->db, folder->Directory, normalized_path );
2321 msi_free( folder->ResolvedTarget );
2322 folder->ResolvedTarget = normalized_path;
2324 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2326 child = fl->folder;
2327 msi_resolve_target_folder( package, child->Directory, load_prop );
2329 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2332 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2334 static const WCHAR query[] = {
2335 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2336 '`','C','o','n','d','i','t','i','o','n','`',0};
2337 static const WCHAR szOutOfDiskSpace[] = {
2338 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2339 MSICOMPONENT *comp;
2340 MSIQUERY *view;
2341 LPWSTR level;
2342 UINT rc;
2344 TRACE("Building directory properties\n");
2345 msi_resolve_target_folder( package, szTargetDir, TRUE );
2347 TRACE("Evaluating component conditions\n");
2348 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2350 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2352 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2353 comp->Enabled = FALSE;
2355 else
2356 comp->Enabled = TRUE;
2359 /* read components states from the registry */
2360 ACTION_GetComponentInstallStates(package);
2361 ACTION_GetFeatureInstallStates(package);
2363 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2365 TRACE("Evaluating feature conditions\n");
2367 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2368 if (rc == ERROR_SUCCESS)
2370 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2371 msiobj_release( &view->hdr );
2372 if (rc != ERROR_SUCCESS)
2373 return rc;
2377 TRACE("Calculating file cost\n");
2378 calculate_file_cost( package );
2380 msi_set_property( package->db, szCostingComplete, szOne );
2381 /* set default run level if not set */
2382 level = msi_dup_property( package->db, szInstallLevel );
2383 if (!level)
2384 msi_set_property( package->db, szInstallLevel, szOne );
2385 msi_free(level);
2387 /* FIXME: check volume disk space */
2388 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2390 return MSI_SetFeatureStates(package);
2393 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, DWORD *size)
2395 LPSTR data = NULL;
2397 if (!value)
2399 data = (LPSTR)strdupW(szEmpty);
2400 *size = sizeof(szEmpty);
2401 *type = REG_SZ;
2402 return data;
2404 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2406 if (value[1]=='x')
2408 LPWSTR ptr;
2409 CHAR byte[5];
2410 LPWSTR deformated = NULL;
2411 int count;
2413 deformat_string(package, &value[2], &deformated);
2415 /* binary value type */
2416 ptr = deformated;
2417 *type = REG_BINARY;
2418 if (strlenW(ptr)%2)
2419 *size = (strlenW(ptr)/2)+1;
2420 else
2421 *size = strlenW(ptr)/2;
2423 data = msi_alloc(*size);
2425 byte[0] = '0';
2426 byte[1] = 'x';
2427 byte[4] = 0;
2428 count = 0;
2429 /* if uneven pad with a zero in front */
2430 if (strlenW(ptr)%2)
2432 byte[2]= '0';
2433 byte[3]= *ptr;
2434 ptr++;
2435 data[count] = (BYTE)strtol(byte,NULL,0);
2436 count ++;
2437 TRACE("Uneven byte count\n");
2439 while (*ptr)
2441 byte[2]= *ptr;
2442 ptr++;
2443 byte[3]= *ptr;
2444 ptr++;
2445 data[count] = (BYTE)strtol(byte,NULL,0);
2446 count ++;
2448 msi_free(deformated);
2450 TRACE("Data %i bytes(%i)\n",*size,count);
2452 else
2454 LPWSTR deformated;
2455 LPWSTR p;
2456 DWORD d = 0;
2457 deformat_string(package, &value[1], &deformated);
2459 *type=REG_DWORD;
2460 *size = sizeof(DWORD);
2461 data = msi_alloc(*size);
2462 p = deformated;
2463 if (*p == '-')
2464 p++;
2465 while (*p)
2467 if ( (*p < '0') || (*p > '9') )
2468 break;
2469 d *= 10;
2470 d += (*p - '0');
2471 p++;
2473 if (deformated[0] == '-')
2474 d = -d;
2475 *(LPDWORD)data = d;
2476 TRACE("DWORD %i\n",*(LPDWORD)data);
2478 msi_free(deformated);
2481 else
2483 static const WCHAR szMulti[] = {'[','~',']',0};
2484 LPCWSTR ptr;
2485 *type=REG_SZ;
2487 if (value[0]=='#')
2489 if (value[1]=='%')
2491 ptr = &value[2];
2492 *type=REG_EXPAND_SZ;
2494 else
2495 ptr = &value[1];
2497 else
2498 ptr=value;
2500 if (strstrW(value, szMulti))
2501 *type = REG_MULTI_SZ;
2503 /* remove initial delimiter */
2504 if (!strncmpW(value, szMulti, 3))
2505 ptr = value + 3;
2507 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2509 /* add double NULL terminator */
2510 if (*type == REG_MULTI_SZ)
2512 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2513 data = msi_realloc_zero(data, *size);
2516 return data;
2519 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2521 const WCHAR *ret;
2523 switch (root)
2525 case -1:
2526 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2528 *root_key = HKEY_LOCAL_MACHINE;
2529 ret = szHLM;
2531 else
2533 *root_key = HKEY_CURRENT_USER;
2534 ret = szHCU;
2536 break;
2537 case 0:
2538 *root_key = HKEY_CLASSES_ROOT;
2539 ret = szHCR;
2540 break;
2541 case 1:
2542 *root_key = HKEY_CURRENT_USER;
2543 ret = szHCU;
2544 break;
2545 case 2:
2546 *root_key = HKEY_LOCAL_MACHINE;
2547 ret = szHLM;
2548 break;
2549 case 3:
2550 *root_key = HKEY_USERS;
2551 ret = szHU;
2552 break;
2553 default:
2554 ERR("Unknown root %i\n", root);
2555 return NULL;
2558 return ret;
2561 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2563 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2564 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2566 if ((is_64bit || is_wow64) &&
2567 !(comp->Attributes & msidbComponentAttributes64bit) &&
2568 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2570 UINT size;
2571 WCHAR *path_32node;
2573 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2574 if (!(path_32node = msi_alloc( size ))) return NULL;
2576 memcpy( path_32node, path, len * sizeof(WCHAR) );
2577 strcpyW( path_32node + len, szWow6432Node );
2578 strcatW( path_32node, szBackSlash );
2579 strcatW( path_32node, path + len );
2580 return path_32node;
2582 return strdupW( path );
2585 static BOOL is_special_entry( const WCHAR *name )
2587 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2590 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2592 MSIPACKAGE *package = param;
2593 LPSTR value;
2594 HKEY root_key, hkey;
2595 DWORD type,size;
2596 LPWSTR deformated, uikey, keypath;
2597 LPCWSTR szRoot, component, name, key;
2598 MSICOMPONENT *comp;
2599 MSIRECORD * uirow;
2600 INT root;
2601 BOOL check_first = FALSE;
2602 UINT rc;
2604 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2606 component = MSI_RecordGetString(row, 6);
2607 comp = msi_get_loaded_component(package,component);
2608 if (!comp)
2609 return ERROR_SUCCESS;
2611 comp->Action = msi_get_component_action( package, comp );
2612 if (comp->Action != INSTALLSTATE_LOCAL)
2614 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2615 return ERROR_SUCCESS;
2618 name = MSI_RecordGetString(row, 4);
2619 if( MSI_RecordIsNull(row,5) && name )
2621 /* null values can have special meanings */
2622 if (name[0]=='-' && name[1] == 0)
2623 return ERROR_SUCCESS;
2624 if ((name[0] == '+' || name[0] == '*') && !name[1])
2625 check_first = TRUE;
2628 root = MSI_RecordGetInteger(row,2);
2629 key = MSI_RecordGetString(row, 3);
2631 szRoot = get_root_key( package, root, &root_key );
2632 if (!szRoot)
2633 return ERROR_SUCCESS;
2635 deformat_string(package, key , &deformated);
2636 size = strlenW(deformated) + strlenW(szRoot) + 1;
2637 uikey = msi_alloc(size*sizeof(WCHAR));
2638 strcpyW(uikey,szRoot);
2639 strcatW(uikey,deformated);
2641 keypath = get_keypath( comp, root_key, deformated );
2642 msi_free( deformated );
2643 if (RegCreateKeyExW( root_key, keypath, 0, NULL, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, NULL, &hkey, NULL ))
2645 ERR("Could not create key %s\n", debugstr_w(keypath));
2646 msi_free(uikey);
2647 msi_free(keypath);
2648 return ERROR_SUCCESS;
2650 value = parse_value(package, MSI_RecordGetString(row, 5), &type, &size);
2651 deformat_string(package, name, &deformated);
2653 if (!is_special_entry( name ))
2655 if (!check_first)
2657 TRACE("Setting value %s of %s\n", debugstr_w(deformated),
2658 debugstr_w(uikey));
2659 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2661 else
2663 DWORD sz = 0;
2664 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2665 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2667 TRACE("value %s of %s checked already exists\n", debugstr_w(deformated),
2668 debugstr_w(uikey));
2670 else
2672 TRACE("Checked and setting value %s of %s\n", debugstr_w(deformated),
2673 debugstr_w(uikey));
2674 if (deformated || size)
2675 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2679 RegCloseKey(hkey);
2681 uirow = MSI_CreateRecord(3);
2682 MSI_RecordSetStringW(uirow,2,deformated);
2683 MSI_RecordSetStringW(uirow,1,uikey);
2684 if (type == REG_SZ || type == REG_EXPAND_SZ)
2685 MSI_RecordSetStringW(uirow, 3, (LPWSTR)value);
2686 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2687 msiobj_release( &uirow->hdr );
2689 msi_free(value);
2690 msi_free(deformated);
2691 msi_free(uikey);
2692 msi_free(keypath);
2694 return ERROR_SUCCESS;
2697 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2699 static const WCHAR query[] = {
2700 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2701 '`','R','e','g','i','s','t','r','y','`',0};
2702 MSIQUERY *view;
2703 UINT rc;
2705 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2706 if (rc != ERROR_SUCCESS)
2707 return ERROR_SUCCESS;
2709 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2710 msiobj_release(&view->hdr);
2711 return rc;
2714 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2716 LONG res;
2717 HKEY hkey;
2718 DWORD num_subkeys, num_values;
2720 if (!(res = RegOpenKeyExW( root, keypath, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &hkey )))
2722 if ((res = RegDeleteValueW( hkey, value )))
2724 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2726 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2727 NULL, NULL, NULL, NULL );
2728 RegCloseKey( hkey );
2729 if (!res && !num_subkeys && !num_values)
2731 TRACE("removing empty key %s\n", debugstr_w(keypath));
2732 RegDeleteKeyExW( root, keypath, KEY_WOW64_64KEY, 0 );
2734 return;
2736 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2739 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2741 HKEY hkey;
2742 LONG res = RegOpenKeyExW( root, keypath, 0, KEY_ALL_ACCESS|KEY_WOW64_64KEY, &hkey );
2743 if (res)
2745 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2746 return;
2748 res = RegDeleteTreeW( hkey, NULL );
2749 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(keypath), res);
2750 res = RegDeleteKeyExW( root, keypath, KEY_WOW64_64KEY, 0 );
2751 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2752 RegCloseKey( hkey );
2755 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2757 MSIPACKAGE *package = param;
2758 LPCWSTR component, name, key_str, root_key_str;
2759 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2760 MSICOMPONENT *comp;
2761 MSIRECORD *uirow;
2762 BOOL delete_key = FALSE;
2763 HKEY hkey_root;
2764 UINT size;
2765 INT root;
2767 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2769 component = MSI_RecordGetString( row, 6 );
2770 comp = msi_get_loaded_component( package, component );
2771 if (!comp)
2772 return ERROR_SUCCESS;
2774 comp->Action = msi_get_component_action( package, comp );
2775 if (comp->Action != INSTALLSTATE_ABSENT)
2777 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2778 return ERROR_SUCCESS;
2781 name = MSI_RecordGetString( row, 4 );
2782 if (MSI_RecordIsNull( row, 5 ) && name )
2784 if (name[0] == '+' && !name[1])
2785 return ERROR_SUCCESS;
2786 if ((name[0] == '-' || name[0] == '*') && !name[1])
2788 delete_key = TRUE;
2789 name = NULL;
2793 root = MSI_RecordGetInteger( row, 2 );
2794 key_str = MSI_RecordGetString( row, 3 );
2796 root_key_str = get_root_key( package, root, &hkey_root );
2797 if (!root_key_str)
2798 return ERROR_SUCCESS;
2800 deformat_string( package, key_str, &deformated_key );
2801 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2802 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2803 strcpyW( ui_key_str, root_key_str );
2804 strcatW( ui_key_str, deformated_key );
2806 deformat_string( package, name, &deformated_name );
2808 keypath = get_keypath( comp, hkey_root, deformated_key );
2809 msi_free( deformated_key );
2810 if (delete_key) delete_reg_key( hkey_root, keypath );
2811 else delete_reg_value( hkey_root, keypath, deformated_name );
2812 msi_free( keypath );
2814 uirow = MSI_CreateRecord( 2 );
2815 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2816 MSI_RecordSetStringW( uirow, 2, deformated_name );
2817 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2818 msiobj_release( &uirow->hdr );
2820 msi_free( ui_key_str );
2821 msi_free( deformated_name );
2822 return ERROR_SUCCESS;
2825 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2827 MSIPACKAGE *package = param;
2828 LPCWSTR component, name, key_str, root_key_str;
2829 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2830 MSICOMPONENT *comp;
2831 MSIRECORD *uirow;
2832 BOOL delete_key = FALSE;
2833 HKEY hkey_root;
2834 UINT size;
2835 INT root;
2837 component = MSI_RecordGetString( row, 5 );
2838 comp = msi_get_loaded_component( package, component );
2839 if (!comp)
2840 return ERROR_SUCCESS;
2842 comp->Action = msi_get_component_action( package, comp );
2843 if (comp->Action != INSTALLSTATE_LOCAL)
2845 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2846 return ERROR_SUCCESS;
2849 if ((name = MSI_RecordGetString( row, 4 )))
2851 if (name[0] == '-' && !name[1])
2853 delete_key = TRUE;
2854 name = NULL;
2858 root = MSI_RecordGetInteger( row, 2 );
2859 key_str = MSI_RecordGetString( row, 3 );
2861 root_key_str = get_root_key( package, root, &hkey_root );
2862 if (!root_key_str)
2863 return ERROR_SUCCESS;
2865 deformat_string( package, key_str, &deformated_key );
2866 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2867 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2868 strcpyW( ui_key_str, root_key_str );
2869 strcatW( ui_key_str, deformated_key );
2871 deformat_string( package, name, &deformated_name );
2873 keypath = get_keypath( comp, hkey_root, deformated_key );
2874 msi_free( deformated_key );
2875 if (delete_key) delete_reg_key( hkey_root, keypath );
2876 else delete_reg_value( hkey_root, keypath, deformated_name );
2877 msi_free( keypath );
2879 uirow = MSI_CreateRecord( 2 );
2880 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2881 MSI_RecordSetStringW( uirow, 2, deformated_name );
2882 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2883 msiobj_release( &uirow->hdr );
2885 msi_free( ui_key_str );
2886 msi_free( deformated_name );
2887 return ERROR_SUCCESS;
2890 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2892 static const WCHAR registry_query[] = {
2893 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2894 '`','R','e','g','i','s','t','r','y','`',0};
2895 static const WCHAR remove_registry_query[] = {
2896 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2897 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2898 MSIQUERY *view;
2899 UINT rc;
2901 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2902 if (rc == ERROR_SUCCESS)
2904 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2905 msiobj_release( &view->hdr );
2906 if (rc != ERROR_SUCCESS)
2907 return rc;
2909 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2910 if (rc == ERROR_SUCCESS)
2912 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2913 msiobj_release( &view->hdr );
2914 if (rc != ERROR_SUCCESS)
2915 return rc;
2917 return ERROR_SUCCESS;
2920 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2922 package->script->CurrentlyScripting = TRUE;
2924 return ERROR_SUCCESS;
2928 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2930 static const WCHAR query[]= {
2931 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2932 '`','R','e','g','i','s','t','r','y','`',0};
2933 MSICOMPONENT *comp;
2934 DWORD total = 0, count = 0;
2935 MSIQUERY *view;
2936 MSIFEATURE *feature;
2937 MSIFILE *file;
2938 UINT rc;
2940 TRACE("InstallValidate\n");
2942 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2943 if (rc == ERROR_SUCCESS)
2945 rc = MSI_IterateRecords( view, &count, NULL, package );
2946 msiobj_release( &view->hdr );
2947 if (rc != ERROR_SUCCESS)
2948 return rc;
2949 total += count * REG_PROGRESS_VALUE;
2951 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2952 total += COMPONENT_PROGRESS_VALUE;
2954 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2955 total += file->FileSize;
2957 msi_ui_progress( package, 0, total, 0, 0 );
2959 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2961 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2962 debugstr_w(feature->Feature), feature->Installed,
2963 feature->ActionRequest, feature->Action);
2965 return ERROR_SUCCESS;
2968 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2970 MSIPACKAGE* package = param;
2971 LPCWSTR cond = NULL;
2972 LPCWSTR message = NULL;
2973 UINT r;
2975 static const WCHAR title[]=
2976 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2978 cond = MSI_RecordGetString(row,1);
2980 r = MSI_EvaluateConditionW(package,cond);
2981 if (r == MSICONDITION_FALSE)
2983 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2985 LPWSTR deformated;
2986 message = MSI_RecordGetString(row,2);
2987 deformat_string(package,message,&deformated);
2988 MessageBoxW(NULL,deformated,title,MB_OK);
2989 msi_free(deformated);
2992 return ERROR_INSTALL_FAILURE;
2995 return ERROR_SUCCESS;
2998 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3000 static const WCHAR query[] = {
3001 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3002 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3003 MSIQUERY *view;
3004 UINT rc;
3006 TRACE("Checking launch conditions\n");
3008 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3009 if (rc != ERROR_SUCCESS)
3010 return ERROR_SUCCESS;
3012 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3013 msiobj_release(&view->hdr);
3014 return rc;
3017 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3020 if (!cmp->KeyPath)
3021 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3023 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3025 static const WCHAR query[] = {
3026 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3027 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3028 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3029 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3030 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3031 MSIRECORD *row;
3032 UINT root, len;
3033 LPWSTR deformated, buffer, deformated_name;
3034 LPCWSTR key, name;
3036 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3037 if (!row)
3038 return NULL;
3040 root = MSI_RecordGetInteger(row,2);
3041 key = MSI_RecordGetString(row, 3);
3042 name = MSI_RecordGetString(row, 4);
3043 deformat_string(package, key , &deformated);
3044 deformat_string(package, name, &deformated_name);
3046 len = strlenW(deformated) + 6;
3047 if (deformated_name)
3048 len+=strlenW(deformated_name);
3050 buffer = msi_alloc( len *sizeof(WCHAR));
3052 if (deformated_name)
3053 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3054 else
3055 sprintfW(buffer,fmt,root,deformated);
3057 msi_free(deformated);
3058 msi_free(deformated_name);
3059 msiobj_release(&row->hdr);
3061 return buffer;
3063 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3065 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3066 return NULL;
3068 else
3070 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3072 if (file)
3073 return strdupW( file->TargetPath );
3075 return NULL;
3078 static HKEY openSharedDLLsKey(void)
3080 HKEY hkey=0;
3081 static const WCHAR path[] =
3082 {'S','o','f','t','w','a','r','e','\\',
3083 'M','i','c','r','o','s','o','f','t','\\',
3084 'W','i','n','d','o','w','s','\\',
3085 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3086 'S','h','a','r','e','d','D','L','L','s',0};
3088 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3089 return hkey;
3092 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3094 HKEY hkey;
3095 DWORD count=0;
3096 DWORD type;
3097 DWORD sz = sizeof(count);
3098 DWORD rc;
3100 hkey = openSharedDLLsKey();
3101 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3102 if (rc != ERROR_SUCCESS)
3103 count = 0;
3104 RegCloseKey(hkey);
3105 return count;
3108 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3110 HKEY hkey;
3112 hkey = openSharedDLLsKey();
3113 if (count > 0)
3114 msi_reg_set_val_dword( hkey, path, count );
3115 else
3116 RegDeleteValueW(hkey,path);
3117 RegCloseKey(hkey);
3118 return count;
3121 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3123 MSIFEATURE *feature;
3124 INT count = 0;
3125 BOOL write = FALSE;
3127 /* only refcount DLLs */
3128 if (comp->KeyPath == NULL ||
3129 comp->assembly ||
3130 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3131 comp->Attributes & msidbComponentAttributesODBCDataSource)
3132 write = FALSE;
3133 else
3135 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3136 write = (count > 0);
3138 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3139 write = TRUE;
3142 /* increment counts */
3143 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3145 ComponentList *cl;
3147 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3148 continue;
3150 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3152 if ( cl->component == comp )
3153 count++;
3157 /* decrement counts */
3158 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3160 ComponentList *cl;
3162 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3163 continue;
3165 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3167 if ( cl->component == comp )
3168 count--;
3172 /* ref count all the files in the component */
3173 if (write)
3175 MSIFILE *file;
3177 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3179 if (file->Component == comp)
3180 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3184 /* add a count for permanent */
3185 if (comp->Attributes & msidbComponentAttributesPermanent)
3186 count ++;
3188 comp->RefCount = count;
3190 if (write)
3191 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3194 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3196 if (comp->assembly)
3198 const WCHAR prefixW[] = {'<','\\',0};
3199 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3200 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3202 if (keypath)
3204 strcpyW( keypath, prefixW );
3205 strcatW( keypath, comp->assembly->display_name );
3207 return keypath;
3209 return resolve_keypath( package, comp );
3212 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3214 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3215 UINT rc;
3216 MSICOMPONENT *comp;
3217 HKEY hkey;
3219 TRACE("\n");
3221 squash_guid(package->ProductCode,squished_pc);
3222 msi_set_sourcedir_props(package, FALSE);
3224 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3226 MSIRECORD *uirow;
3227 INSTALLSTATE action;
3229 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3230 if (!comp->ComponentId)
3231 continue;
3233 squash_guid( comp->ComponentId, squished_cc );
3234 msi_free( comp->FullKeypath );
3235 comp->FullKeypath = build_full_keypath( package, comp );
3237 ACTION_RefCountComponent( package, comp );
3239 if (package->need_rollback) action = comp->Installed;
3240 else action = comp->ActionRequest;
3242 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3243 debugstr_w(comp->Component), debugstr_w(squished_cc),
3244 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3246 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3248 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3249 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3250 else
3251 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3253 if (rc != ERROR_SUCCESS)
3254 continue;
3256 if (comp->Attributes & msidbComponentAttributesPermanent)
3258 static const WCHAR szPermKey[] =
3259 { '0','0','0','0','0','0','0','0','0','0','0','0',
3260 '0','0','0','0','0','0','0','0','0','0','0','0',
3261 '0','0','0','0','0','0','0','0',0 };
3263 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3265 if (action == INSTALLSTATE_LOCAL)
3266 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3267 else
3269 MSIFILE *file;
3270 MSIRECORD *row;
3271 LPWSTR ptr, ptr2;
3272 WCHAR source[MAX_PATH];
3273 WCHAR base[MAX_PATH];
3274 LPWSTR sourcepath;
3276 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3277 static const WCHAR query[] = {
3278 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3279 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3280 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3281 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3282 '`','D','i','s','k','I','d','`',0};
3284 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3285 continue;
3287 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3288 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3289 ptr2 = strrchrW(source, '\\') + 1;
3290 msiobj_release(&row->hdr);
3292 lstrcpyW(base, package->PackagePath);
3293 ptr = strrchrW(base, '\\');
3294 *(ptr + 1) = '\0';
3296 sourcepath = msi_resolve_file_source(package, file);
3297 ptr = sourcepath + lstrlenW(base);
3298 lstrcpyW(ptr2, ptr);
3299 msi_free(sourcepath);
3301 msi_reg_set_val_str(hkey, squished_pc, source);
3303 RegCloseKey(hkey);
3305 else if (action == INSTALLSTATE_ABSENT)
3307 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3308 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3309 else
3310 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3313 /* UI stuff */
3314 uirow = MSI_CreateRecord(3);
3315 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3316 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3317 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3318 msi_ui_actiondata( package, szProcessComponents, uirow );
3319 msiobj_release( &uirow->hdr );
3321 return ERROR_SUCCESS;
3324 typedef struct {
3325 CLSID clsid;
3326 LPWSTR source;
3328 LPWSTR path;
3329 ITypeLib *ptLib;
3330 } typelib_struct;
3332 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3333 LPWSTR lpszName, LONG_PTR lParam)
3335 TLIBATTR *attr;
3336 typelib_struct *tl_struct = (typelib_struct*) lParam;
3337 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3338 int sz;
3339 HRESULT res;
3341 if (!IS_INTRESOURCE(lpszName))
3343 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3344 return TRUE;
3347 sz = strlenW(tl_struct->source)+4;
3348 sz *= sizeof(WCHAR);
3350 if ((INT_PTR)lpszName == 1)
3351 tl_struct->path = strdupW(tl_struct->source);
3352 else
3354 tl_struct->path = msi_alloc(sz);
3355 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3358 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3359 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3360 if (FAILED(res))
3362 msi_free(tl_struct->path);
3363 tl_struct->path = NULL;
3365 return TRUE;
3368 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3369 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3371 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3372 return FALSE;
3375 msi_free(tl_struct->path);
3376 tl_struct->path = NULL;
3378 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3379 ITypeLib_Release(tl_struct->ptLib);
3381 return TRUE;
3384 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3386 MSIPACKAGE* package = param;
3387 LPCWSTR component;
3388 MSICOMPONENT *comp;
3389 MSIFILE *file;
3390 typelib_struct tl_struct;
3391 ITypeLib *tlib;
3392 HMODULE module;
3393 HRESULT hr;
3395 component = MSI_RecordGetString(row,3);
3396 comp = msi_get_loaded_component(package,component);
3397 if (!comp)
3398 return ERROR_SUCCESS;
3400 comp->Action = msi_get_component_action( package, comp );
3401 if (comp->Action != INSTALLSTATE_LOCAL)
3403 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3404 return ERROR_SUCCESS;
3407 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3409 TRACE("component has no key path\n");
3410 return ERROR_SUCCESS;
3412 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3414 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3415 if (module)
3417 LPCWSTR guid;
3418 guid = MSI_RecordGetString(row,1);
3419 CLSIDFromString( guid, &tl_struct.clsid);
3420 tl_struct.source = strdupW( file->TargetPath );
3421 tl_struct.path = NULL;
3423 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3424 (LONG_PTR)&tl_struct);
3426 if (tl_struct.path)
3428 LPCWSTR helpid, help_path = NULL;
3429 HRESULT res;
3431 helpid = MSI_RecordGetString(row,6);
3433 if (helpid) help_path = msi_get_target_folder( package, helpid );
3434 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3436 if (FAILED(res))
3437 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3438 else
3439 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3441 ITypeLib_Release(tl_struct.ptLib);
3442 msi_free(tl_struct.path);
3444 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3446 FreeLibrary(module);
3447 msi_free(tl_struct.source);
3449 else
3451 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3452 if (FAILED(hr))
3454 ERR("Failed to load type library: %08x\n", hr);
3455 return ERROR_INSTALL_FAILURE;
3458 ITypeLib_Release(tlib);
3461 return ERROR_SUCCESS;
3464 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3466 static const WCHAR query[] = {
3467 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3468 '`','T','y','p','e','L','i','b','`',0};
3469 MSIQUERY *view;
3470 UINT rc;
3472 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3473 if (rc != ERROR_SUCCESS)
3474 return ERROR_SUCCESS;
3476 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3477 msiobj_release(&view->hdr);
3478 return rc;
3481 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3483 MSIPACKAGE *package = param;
3484 LPCWSTR component, guid;
3485 MSICOMPONENT *comp;
3486 GUID libid;
3487 UINT version;
3488 LCID language;
3489 SYSKIND syskind;
3490 HRESULT hr;
3492 component = MSI_RecordGetString( row, 3 );
3493 comp = msi_get_loaded_component( package, component );
3494 if (!comp)
3495 return ERROR_SUCCESS;
3497 comp->Action = msi_get_component_action( package, comp );
3498 if (comp->Action != INSTALLSTATE_ABSENT)
3500 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3501 return ERROR_SUCCESS;
3503 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3505 guid = MSI_RecordGetString( row, 1 );
3506 CLSIDFromString( guid, &libid );
3507 version = MSI_RecordGetInteger( row, 4 );
3508 language = MSI_RecordGetInteger( row, 2 );
3510 #ifdef _WIN64
3511 syskind = SYS_WIN64;
3512 #else
3513 syskind = SYS_WIN32;
3514 #endif
3516 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3517 if (FAILED(hr))
3519 WARN("Failed to unregister typelib: %08x\n", hr);
3522 return ERROR_SUCCESS;
3525 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3527 static const WCHAR query[] = {
3528 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3529 '`','T','y','p','e','L','i','b','`',0};
3530 MSIQUERY *view;
3531 UINT rc;
3533 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3534 if (rc != ERROR_SUCCESS)
3535 return ERROR_SUCCESS;
3537 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3538 msiobj_release( &view->hdr );
3539 return rc;
3542 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3544 static const WCHAR szlnk[] = {'.','l','n','k',0};
3545 LPCWSTR directory, extension, link_folder;
3546 LPWSTR link_file, filename;
3548 directory = MSI_RecordGetString( row, 2 );
3549 link_folder = msi_get_target_folder( package, directory );
3550 if (!link_folder)
3552 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3553 return NULL;
3555 /* may be needed because of a bug somewhere else */
3556 msi_create_full_path( link_folder );
3558 filename = msi_dup_record_field( row, 3 );
3559 msi_reduce_to_long_filename( filename );
3561 extension = strchrW( filename, '.' );
3562 if (!extension || strcmpiW( extension, szlnk ))
3564 int len = strlenW( filename );
3565 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3566 memcpy( filename + len, szlnk, sizeof(szlnk) );
3568 link_file = msi_build_directory_name( 2, link_folder, filename );
3569 msi_free( filename );
3571 return link_file;
3574 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3576 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3577 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3578 WCHAR *folder, *dest, *path;
3580 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3581 folder = msi_dup_property( package->db, szWindowsFolder );
3582 else
3584 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3585 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3586 msi_free( appdata );
3588 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3589 msi_create_full_path( dest );
3590 path = msi_build_directory_name( 2, dest, icon_name );
3591 msi_free( folder );
3592 msi_free( dest );
3593 return path;
3596 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3598 MSIPACKAGE *package = param;
3599 LPWSTR link_file, deformated, path;
3600 LPCWSTR component, target;
3601 MSICOMPONENT *comp;
3602 IShellLinkW *sl = NULL;
3603 IPersistFile *pf = NULL;
3604 HRESULT res;
3606 component = MSI_RecordGetString(row, 4);
3607 comp = msi_get_loaded_component(package, component);
3608 if (!comp)
3609 return ERROR_SUCCESS;
3611 comp->Action = msi_get_component_action( package, comp );
3612 if (comp->Action != INSTALLSTATE_LOCAL)
3614 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3615 return ERROR_SUCCESS;
3617 msi_ui_actiondata( package, szCreateShortcuts, row );
3619 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3620 &IID_IShellLinkW, (LPVOID *) &sl );
3622 if (FAILED( res ))
3624 ERR("CLSID_ShellLink not available\n");
3625 goto err;
3628 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3629 if (FAILED( res ))
3631 ERR("QueryInterface(IID_IPersistFile) failed\n");
3632 goto err;
3635 target = MSI_RecordGetString(row, 5);
3636 if (strchrW(target, '['))
3638 deformat_string( package, target, &path );
3639 TRACE("target path is %s\n", debugstr_w(path));
3640 IShellLinkW_SetPath( sl, path );
3641 msi_free( path );
3643 else
3645 FIXME("poorly handled shortcut format, advertised shortcut\n");
3646 IShellLinkW_SetPath(sl,comp->FullKeypath);
3649 if (!MSI_RecordIsNull(row,6))
3651 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3652 deformat_string(package, arguments, &deformated);
3653 IShellLinkW_SetArguments(sl,deformated);
3654 msi_free(deformated);
3657 if (!MSI_RecordIsNull(row,7))
3659 LPCWSTR description = MSI_RecordGetString(row, 7);
3660 IShellLinkW_SetDescription(sl, description);
3663 if (!MSI_RecordIsNull(row,8))
3664 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3666 if (!MSI_RecordIsNull(row,9))
3668 INT index;
3669 LPCWSTR icon = MSI_RecordGetString(row, 9);
3671 path = msi_build_icon_path(package, icon);
3672 index = MSI_RecordGetInteger(row,10);
3674 /* no value means 0 */
3675 if (index == MSI_NULL_INTEGER)
3676 index = 0;
3678 IShellLinkW_SetIconLocation(sl, path, index);
3679 msi_free(path);
3682 if (!MSI_RecordIsNull(row,11))
3683 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3685 if (!MSI_RecordIsNull(row,12))
3687 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3688 full_path = msi_get_target_folder( package, wkdir );
3689 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3691 link_file = get_link_file(package, row);
3693 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3694 IPersistFile_Save(pf, link_file, FALSE);
3695 msi_free(link_file);
3697 err:
3698 if (pf)
3699 IPersistFile_Release( pf );
3700 if (sl)
3701 IShellLinkW_Release( sl );
3703 return ERROR_SUCCESS;
3706 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3708 static const WCHAR query[] = {
3709 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3710 '`','S','h','o','r','t','c','u','t','`',0};
3711 MSIQUERY *view;
3712 HRESULT res;
3713 UINT rc;
3715 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3716 if (rc != ERROR_SUCCESS)
3717 return ERROR_SUCCESS;
3719 res = CoInitialize( NULL );
3721 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3722 msiobj_release(&view->hdr);
3724 if (SUCCEEDED(res)) CoUninitialize();
3725 return rc;
3728 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3730 MSIPACKAGE *package = param;
3731 LPWSTR link_file;
3732 LPCWSTR component;
3733 MSICOMPONENT *comp;
3735 component = MSI_RecordGetString( row, 4 );
3736 comp = msi_get_loaded_component( package, component );
3737 if (!comp)
3738 return ERROR_SUCCESS;
3740 comp->Action = msi_get_component_action( package, comp );
3741 if (comp->Action != INSTALLSTATE_ABSENT)
3743 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3744 return ERROR_SUCCESS;
3746 msi_ui_actiondata( package, szRemoveShortcuts, row );
3748 link_file = get_link_file( package, row );
3750 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3751 if (!DeleteFileW( link_file ))
3753 WARN("Failed to remove shortcut file %u\n", GetLastError());
3755 msi_free( link_file );
3757 return ERROR_SUCCESS;
3760 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3762 static const WCHAR query[] = {
3763 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3764 '`','S','h','o','r','t','c','u','t','`',0};
3765 MSIQUERY *view;
3766 UINT rc;
3768 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3769 if (rc != ERROR_SUCCESS)
3770 return ERROR_SUCCESS;
3772 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3773 msiobj_release( &view->hdr );
3774 return rc;
3777 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3779 MSIPACKAGE* package = param;
3780 HANDLE the_file;
3781 LPWSTR FilePath;
3782 LPCWSTR FileName;
3783 CHAR buffer[1024];
3784 DWORD sz;
3785 UINT rc;
3787 FileName = MSI_RecordGetString(row,1);
3788 if (!FileName)
3790 ERR("Unable to get FileName\n");
3791 return ERROR_SUCCESS;
3794 FilePath = msi_build_icon_path(package, FileName);
3796 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3798 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3799 FILE_ATTRIBUTE_NORMAL, NULL);
3801 if (the_file == INVALID_HANDLE_VALUE)
3803 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3804 msi_free(FilePath);
3805 return ERROR_SUCCESS;
3810 DWORD write;
3811 sz = 1024;
3812 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3813 if (rc != ERROR_SUCCESS)
3815 ERR("Failed to get stream\n");
3816 CloseHandle(the_file);
3817 DeleteFileW(FilePath);
3818 break;
3820 WriteFile(the_file,buffer,sz,&write,NULL);
3821 } while (sz == 1024);
3823 msi_free(FilePath);
3824 CloseHandle(the_file);
3826 return ERROR_SUCCESS;
3829 static UINT msi_publish_icons(MSIPACKAGE *package)
3831 static const WCHAR query[]= {
3832 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3833 '`','I','c','o','n','`',0};
3834 MSIQUERY *view;
3835 UINT r;
3837 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3838 if (r == ERROR_SUCCESS)
3840 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3841 msiobj_release(&view->hdr);
3842 if (r != ERROR_SUCCESS)
3843 return r;
3845 return ERROR_SUCCESS;
3848 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3850 UINT r;
3851 HKEY source;
3852 LPWSTR buffer;
3853 MSIMEDIADISK *disk;
3854 MSISOURCELISTINFO *info;
3856 r = RegCreateKeyW(hkey, szSourceList, &source);
3857 if (r != ERROR_SUCCESS)
3858 return r;
3860 RegCloseKey(source);
3862 buffer = strrchrW(package->PackagePath, '\\') + 1;
3863 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3864 package->Context, MSICODE_PRODUCT,
3865 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3866 if (r != ERROR_SUCCESS)
3867 return r;
3869 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3870 package->Context, MSICODE_PRODUCT,
3871 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3872 if (r != ERROR_SUCCESS)
3873 return r;
3875 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3876 package->Context, MSICODE_PRODUCT,
3877 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3878 if (r != ERROR_SUCCESS)
3879 return r;
3881 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3883 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3884 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3885 info->options, info->value);
3886 else
3887 MsiSourceListSetInfoW(package->ProductCode, NULL,
3888 info->context, info->options,
3889 info->property, info->value);
3892 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3894 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3895 disk->context, disk->options,
3896 disk->disk_id, disk->volume_label, disk->disk_prompt);
3899 return ERROR_SUCCESS;
3902 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3904 MSIHANDLE hdb, suminfo;
3905 WCHAR guids[MAX_PATH];
3906 WCHAR packcode[SQUISH_GUID_SIZE];
3907 LPWSTR buffer;
3908 LPWSTR ptr;
3909 DWORD langid;
3910 DWORD size;
3911 UINT r;
3913 static const WCHAR szARPProductIcon[] =
3914 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3915 static const WCHAR szAssignment[] =
3916 {'A','s','s','i','g','n','m','e','n','t',0};
3917 static const WCHAR szAdvertiseFlags[] =
3918 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3919 static const WCHAR szClients[] =
3920 {'C','l','i','e','n','t','s',0};
3921 static const WCHAR szColon[] = {':',0};
3923 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3924 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3925 msi_free(buffer);
3927 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3928 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3930 /* FIXME */
3931 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3933 buffer = msi_dup_property(package->db, szARPProductIcon);
3934 if (buffer)
3936 LPWSTR path = msi_build_icon_path(package, buffer);
3937 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3938 msi_free(path);
3939 msi_free(buffer);
3942 buffer = msi_dup_property(package->db, szProductVersion);
3943 if (buffer)
3945 DWORD verdword = msi_version_str_to_dword(buffer);
3946 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3947 msi_free(buffer);
3950 msi_reg_set_val_dword(hkey, szAssignment, 0);
3951 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3952 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3953 msi_reg_set_val_str(hkey, szClients, szColon);
3955 hdb = alloc_msihandle(&package->db->hdr);
3956 if (!hdb)
3957 return ERROR_NOT_ENOUGH_MEMORY;
3959 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3960 MsiCloseHandle(hdb);
3961 if (r != ERROR_SUCCESS)
3962 goto done;
3964 size = MAX_PATH;
3965 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3966 NULL, guids, &size);
3967 if (r != ERROR_SUCCESS)
3968 goto done;
3970 ptr = strchrW(guids, ';');
3971 if (ptr) *ptr = 0;
3972 squash_guid(guids, packcode);
3973 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3975 done:
3976 MsiCloseHandle(suminfo);
3977 return ERROR_SUCCESS;
3980 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3982 UINT r;
3983 HKEY hkey;
3984 LPWSTR upgrade;
3985 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3987 upgrade = msi_dup_property(package->db, szUpgradeCode);
3988 if (!upgrade)
3989 return ERROR_SUCCESS;
3991 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3992 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3993 else
3994 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3996 if (r != ERROR_SUCCESS)
3998 WARN("failed to open upgrade code key\n");
3999 msi_free(upgrade);
4000 return ERROR_SUCCESS;
4002 squash_guid(package->ProductCode, squashed_pc);
4003 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4004 RegCloseKey(hkey);
4005 msi_free(upgrade);
4006 return ERROR_SUCCESS;
4009 static BOOL msi_check_publish(MSIPACKAGE *package)
4011 MSIFEATURE *feature;
4013 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4015 feature->Action = msi_get_feature_action( package, feature );
4016 if (feature->Action == INSTALLSTATE_LOCAL)
4017 return TRUE;
4020 return FALSE;
4023 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4025 MSIFEATURE *feature;
4027 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4029 feature->Action = msi_get_feature_action( package, feature );
4030 if (feature->Action != INSTALLSTATE_ABSENT)
4031 return FALSE;
4034 return TRUE;
4037 static UINT msi_publish_patches( MSIPACKAGE *package )
4039 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4040 WCHAR patch_squashed[GUID_SIZE];
4041 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4042 LONG res;
4043 MSIPATCHINFO *patch;
4044 UINT r;
4045 WCHAR *p, *all_patches = NULL;
4046 DWORD len = 0;
4048 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4049 if (r != ERROR_SUCCESS)
4050 return ERROR_FUNCTION_FAILED;
4052 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4053 if (res != ERROR_SUCCESS)
4055 r = ERROR_FUNCTION_FAILED;
4056 goto done;
4059 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4060 if (r != ERROR_SUCCESS)
4061 goto done;
4063 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4065 squash_guid( patch->patchcode, patch_squashed );
4066 len += strlenW( patch_squashed ) + 1;
4069 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4070 if (!all_patches)
4071 goto done;
4073 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4075 HKEY patch_key;
4077 squash_guid( patch->patchcode, p );
4078 p += strlenW( p ) + 1;
4080 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4081 (const BYTE *)patch->transforms,
4082 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4083 if (res != ERROR_SUCCESS)
4084 goto done;
4086 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4087 if (r != ERROR_SUCCESS)
4088 goto done;
4090 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4091 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4092 RegCloseKey( patch_key );
4093 if (res != ERROR_SUCCESS)
4094 goto done;
4096 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4098 res = GetLastError();
4099 ERR("Unable to copy patch package %d\n", res);
4100 goto done;
4102 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4103 if (res != ERROR_SUCCESS)
4104 goto done;
4106 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4107 RegCloseKey( patch_key );
4108 if (res != ERROR_SUCCESS)
4109 goto done;
4112 all_patches[len] = 0;
4113 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4114 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4115 if (res != ERROR_SUCCESS)
4116 goto done;
4118 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4119 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4120 if (res != ERROR_SUCCESS)
4121 r = ERROR_FUNCTION_FAILED;
4123 done:
4124 RegCloseKey( product_patches_key );
4125 RegCloseKey( patches_key );
4126 RegCloseKey( product_key );
4127 msi_free( all_patches );
4128 return r;
4131 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4133 UINT rc;
4134 HKEY hukey = NULL, hudkey = NULL;
4135 MSIRECORD *uirow;
4137 if (!list_empty(&package->patches))
4139 rc = msi_publish_patches(package);
4140 if (rc != ERROR_SUCCESS)
4141 goto end;
4144 /* FIXME: also need to publish if the product is in advertise mode */
4145 if (!msi_check_publish(package))
4146 return ERROR_SUCCESS;
4148 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4149 &hukey, TRUE);
4150 if (rc != ERROR_SUCCESS)
4151 goto end;
4153 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4154 NULL, &hudkey, TRUE);
4155 if (rc != ERROR_SUCCESS)
4156 goto end;
4158 rc = msi_publish_upgrade_code(package);
4159 if (rc != ERROR_SUCCESS)
4160 goto end;
4162 rc = msi_publish_product_properties(package, hukey);
4163 if (rc != ERROR_SUCCESS)
4164 goto end;
4166 rc = msi_publish_sourcelist(package, hukey);
4167 if (rc != ERROR_SUCCESS)
4168 goto end;
4170 rc = msi_publish_icons(package);
4172 end:
4173 uirow = MSI_CreateRecord( 1 );
4174 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4175 msi_ui_actiondata( package, szPublishProduct, uirow );
4176 msiobj_release( &uirow->hdr );
4178 RegCloseKey(hukey);
4179 RegCloseKey(hudkey);
4180 return rc;
4183 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4185 WCHAR *filename, *ptr, *folder, *ret;
4186 const WCHAR *dirprop;
4188 filename = msi_dup_record_field( row, 2 );
4189 if (filename && (ptr = strchrW( filename, '|' )))
4190 ptr++;
4191 else
4192 ptr = filename;
4194 dirprop = MSI_RecordGetString( row, 3 );
4195 if (dirprop)
4197 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4198 if (!folder) folder = msi_dup_property( package->db, dirprop );
4200 else
4201 folder = msi_dup_property( package->db, szWindowsFolder );
4203 if (!folder)
4205 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4206 msi_free( filename );
4207 return NULL;
4210 ret = msi_build_directory_name( 2, folder, ptr );
4212 msi_free( filename );
4213 msi_free( folder );
4214 return ret;
4217 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4219 MSIPACKAGE *package = param;
4220 LPCWSTR component, section, key, value, identifier;
4221 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4222 MSIRECORD * uirow;
4223 INT action;
4224 MSICOMPONENT *comp;
4226 component = MSI_RecordGetString(row, 8);
4227 comp = msi_get_loaded_component(package,component);
4228 if (!comp)
4229 return ERROR_SUCCESS;
4231 comp->Action = msi_get_component_action( package, comp );
4232 if (comp->Action != INSTALLSTATE_LOCAL)
4234 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4235 return ERROR_SUCCESS;
4238 identifier = MSI_RecordGetString(row,1);
4239 section = MSI_RecordGetString(row,4);
4240 key = MSI_RecordGetString(row,5);
4241 value = MSI_RecordGetString(row,6);
4242 action = MSI_RecordGetInteger(row,7);
4244 deformat_string(package,section,&deformated_section);
4245 deformat_string(package,key,&deformated_key);
4246 deformat_string(package,value,&deformated_value);
4248 fullname = get_ini_file_name(package, row);
4250 if (action == 0)
4252 TRACE("Adding value %s to section %s in %s\n",
4253 debugstr_w(deformated_key), debugstr_w(deformated_section),
4254 debugstr_w(fullname));
4255 WritePrivateProfileStringW(deformated_section, deformated_key,
4256 deformated_value, fullname);
4258 else if (action == 1)
4260 WCHAR returned[10];
4261 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4262 returned, 10, fullname);
4263 if (returned[0] == 0)
4265 TRACE("Adding value %s to section %s in %s\n",
4266 debugstr_w(deformated_key), debugstr_w(deformated_section),
4267 debugstr_w(fullname));
4269 WritePrivateProfileStringW(deformated_section, deformated_key,
4270 deformated_value, fullname);
4273 else if (action == 3)
4274 FIXME("Append to existing section not yet implemented\n");
4276 uirow = MSI_CreateRecord(4);
4277 MSI_RecordSetStringW(uirow,1,identifier);
4278 MSI_RecordSetStringW(uirow,2,deformated_section);
4279 MSI_RecordSetStringW(uirow,3,deformated_key);
4280 MSI_RecordSetStringW(uirow,4,deformated_value);
4281 msi_ui_actiondata( package, szWriteIniValues, uirow );
4282 msiobj_release( &uirow->hdr );
4284 msi_free(fullname);
4285 msi_free(deformated_key);
4286 msi_free(deformated_value);
4287 msi_free(deformated_section);
4288 return ERROR_SUCCESS;
4291 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4293 static const WCHAR query[] = {
4294 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4295 '`','I','n','i','F','i','l','e','`',0};
4296 MSIQUERY *view;
4297 UINT rc;
4299 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4300 if (rc != ERROR_SUCCESS)
4301 return ERROR_SUCCESS;
4303 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4304 msiobj_release(&view->hdr);
4305 return rc;
4308 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4310 MSIPACKAGE *package = param;
4311 LPCWSTR component, section, key, value, identifier;
4312 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4313 MSICOMPONENT *comp;
4314 MSIRECORD *uirow;
4315 INT action;
4317 component = MSI_RecordGetString( row, 8 );
4318 comp = msi_get_loaded_component( package, component );
4319 if (!comp)
4320 return ERROR_SUCCESS;
4322 comp->Action = msi_get_component_action( package, comp );
4323 if (comp->Action != INSTALLSTATE_ABSENT)
4325 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4326 return ERROR_SUCCESS;
4329 identifier = MSI_RecordGetString( row, 1 );
4330 section = MSI_RecordGetString( row, 4 );
4331 key = MSI_RecordGetString( row, 5 );
4332 value = MSI_RecordGetString( row, 6 );
4333 action = MSI_RecordGetInteger( row, 7 );
4335 deformat_string( package, section, &deformated_section );
4336 deformat_string( package, key, &deformated_key );
4337 deformat_string( package, value, &deformated_value );
4339 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4341 filename = get_ini_file_name( package, row );
4343 TRACE("Removing key %s from section %s in %s\n",
4344 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4346 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4348 WARN("Unable to remove key %u\n", GetLastError());
4350 msi_free( filename );
4352 else
4353 FIXME("Unsupported action %d\n", action);
4356 uirow = MSI_CreateRecord( 4 );
4357 MSI_RecordSetStringW( uirow, 1, identifier );
4358 MSI_RecordSetStringW( uirow, 2, deformated_section );
4359 MSI_RecordSetStringW( uirow, 3, deformated_key );
4360 MSI_RecordSetStringW( uirow, 4, deformated_value );
4361 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4362 msiobj_release( &uirow->hdr );
4364 msi_free( deformated_key );
4365 msi_free( deformated_value );
4366 msi_free( deformated_section );
4367 return ERROR_SUCCESS;
4370 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4372 MSIPACKAGE *package = param;
4373 LPCWSTR component, section, key, value, identifier;
4374 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4375 MSICOMPONENT *comp;
4376 MSIRECORD *uirow;
4377 INT action;
4379 component = MSI_RecordGetString( row, 8 );
4380 comp = msi_get_loaded_component( package, component );
4381 if (!comp)
4382 return ERROR_SUCCESS;
4384 comp->Action = msi_get_component_action( package, comp );
4385 if (comp->Action != INSTALLSTATE_LOCAL)
4387 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4388 return ERROR_SUCCESS;
4391 identifier = MSI_RecordGetString( row, 1 );
4392 section = MSI_RecordGetString( row, 4 );
4393 key = MSI_RecordGetString( row, 5 );
4394 value = MSI_RecordGetString( row, 6 );
4395 action = MSI_RecordGetInteger( row, 7 );
4397 deformat_string( package, section, &deformated_section );
4398 deformat_string( package, key, &deformated_key );
4399 deformat_string( package, value, &deformated_value );
4401 if (action == msidbIniFileActionRemoveLine)
4403 filename = get_ini_file_name( package, row );
4405 TRACE("Removing key %s from section %s in %s\n",
4406 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4408 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4410 WARN("Unable to remove key %u\n", GetLastError());
4412 msi_free( filename );
4414 else
4415 FIXME("Unsupported action %d\n", action);
4417 uirow = MSI_CreateRecord( 4 );
4418 MSI_RecordSetStringW( uirow, 1, identifier );
4419 MSI_RecordSetStringW( uirow, 2, deformated_section );
4420 MSI_RecordSetStringW( uirow, 3, deformated_key );
4421 MSI_RecordSetStringW( uirow, 4, deformated_value );
4422 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4423 msiobj_release( &uirow->hdr );
4425 msi_free( deformated_key );
4426 msi_free( deformated_value );
4427 msi_free( deformated_section );
4428 return ERROR_SUCCESS;
4431 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4433 static const WCHAR query[] = {
4434 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4435 '`','I','n','i','F','i','l','e','`',0};
4436 static const WCHAR remove_query[] = {
4437 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4438 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4439 MSIQUERY *view;
4440 UINT rc;
4442 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4443 if (rc == ERROR_SUCCESS)
4445 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4446 msiobj_release( &view->hdr );
4447 if (rc != ERROR_SUCCESS)
4448 return rc;
4450 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4451 if (rc == ERROR_SUCCESS)
4453 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4454 msiobj_release( &view->hdr );
4455 if (rc != ERROR_SUCCESS)
4456 return rc;
4458 return ERROR_SUCCESS;
4461 static void register_dll( const WCHAR *dll, BOOL unregister )
4463 HMODULE hmod;
4465 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4466 if (hmod)
4468 HRESULT (WINAPI *func_ptr)( void );
4469 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4471 func_ptr = (void *)GetProcAddress( hmod, func );
4472 if (func_ptr)
4474 HRESULT hr = func_ptr();
4475 if (FAILED( hr ))
4476 WARN("failed to register dll 0x%08x\n", hr);
4478 else
4479 WARN("entry point %s not found\n", func);
4480 FreeLibrary( hmod );
4481 return;
4483 WARN("failed to load library %u\n", GetLastError());
4486 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4488 MSIPACKAGE *package = param;
4489 LPCWSTR filename;
4490 MSIFILE *file;
4491 MSIRECORD *uirow;
4493 filename = MSI_RecordGetString( row, 1 );
4494 file = msi_get_loaded_file( package, filename );
4495 if (!file)
4497 WARN("unable to find file %s\n", debugstr_w(filename));
4498 return ERROR_SUCCESS;
4500 file->Component->Action = msi_get_component_action( package, file->Component );
4501 if (file->Component->Action != INSTALLSTATE_LOCAL)
4503 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4504 return ERROR_SUCCESS;
4507 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4508 register_dll( file->TargetPath, FALSE );
4510 uirow = MSI_CreateRecord( 2 );
4511 MSI_RecordSetStringW( uirow, 1, file->File );
4512 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4513 msi_ui_actiondata( package, szSelfRegModules, uirow );
4514 msiobj_release( &uirow->hdr );
4516 return ERROR_SUCCESS;
4519 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4521 static const WCHAR query[] = {
4522 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4523 '`','S','e','l','f','R','e','g','`',0};
4524 MSIQUERY *view;
4525 UINT rc;
4527 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4528 if (rc != ERROR_SUCCESS)
4529 return ERROR_SUCCESS;
4531 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4532 msiobj_release(&view->hdr);
4533 return rc;
4536 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4538 MSIPACKAGE *package = param;
4539 LPCWSTR filename;
4540 MSIFILE *file;
4541 MSIRECORD *uirow;
4543 filename = MSI_RecordGetString( row, 1 );
4544 file = msi_get_loaded_file( package, filename );
4545 if (!file)
4547 WARN("unable to find file %s\n", debugstr_w(filename));
4548 return ERROR_SUCCESS;
4550 file->Component->Action = msi_get_component_action( package, file->Component );
4551 if (file->Component->Action != INSTALLSTATE_ABSENT)
4553 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4554 return ERROR_SUCCESS;
4557 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4558 register_dll( file->TargetPath, TRUE );
4560 uirow = MSI_CreateRecord( 2 );
4561 MSI_RecordSetStringW( uirow, 1, file->File );
4562 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4563 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4564 msiobj_release( &uirow->hdr );
4566 return ERROR_SUCCESS;
4569 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4571 static const WCHAR query[] = {
4572 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4573 '`','S','e','l','f','R','e','g','`',0};
4574 MSIQUERY *view;
4575 UINT rc;
4577 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4578 if (rc != ERROR_SUCCESS)
4579 return ERROR_SUCCESS;
4581 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4582 msiobj_release( &view->hdr );
4583 return rc;
4586 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4588 MSIFEATURE *feature;
4589 UINT rc;
4590 HKEY hkey = NULL, userdata = NULL;
4592 if (!msi_check_publish(package))
4593 return ERROR_SUCCESS;
4595 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4596 &hkey, TRUE);
4597 if (rc != ERROR_SUCCESS)
4598 goto end;
4600 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4601 &userdata, TRUE);
4602 if (rc != ERROR_SUCCESS)
4603 goto end;
4605 /* here the guids are base 85 encoded */
4606 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4608 ComponentList *cl;
4609 LPWSTR data = NULL;
4610 GUID clsid;
4611 INT size;
4612 BOOL absent = FALSE;
4613 MSIRECORD *uirow;
4615 if (feature->Action != INSTALLSTATE_LOCAL &&
4616 feature->Action != INSTALLSTATE_SOURCE &&
4617 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4619 size = 1;
4620 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4622 size += 21;
4624 if (feature->Feature_Parent)
4625 size += strlenW( feature->Feature_Parent )+2;
4627 data = msi_alloc(size * sizeof(WCHAR));
4629 data[0] = 0;
4630 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4632 MSICOMPONENT* component = cl->component;
4633 WCHAR buf[21];
4635 buf[0] = 0;
4636 if (component->ComponentId)
4638 TRACE("From %s\n",debugstr_w(component->ComponentId));
4639 CLSIDFromString(component->ComponentId, &clsid);
4640 encode_base85_guid(&clsid,buf);
4641 TRACE("to %s\n",debugstr_w(buf));
4642 strcatW(data,buf);
4646 if (feature->Feature_Parent)
4648 static const WCHAR sep[] = {'\2',0};
4649 strcatW(data,sep);
4650 strcatW(data,feature->Feature_Parent);
4653 msi_reg_set_val_str( userdata, feature->Feature, data );
4654 msi_free(data);
4656 size = 0;
4657 if (feature->Feature_Parent)
4658 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4659 if (!absent)
4661 size += sizeof(WCHAR);
4662 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4663 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4665 else
4667 size += 2*sizeof(WCHAR);
4668 data = msi_alloc(size);
4669 data[0] = 0x6;
4670 data[1] = 0;
4671 if (feature->Feature_Parent)
4672 strcpyW( &data[1], feature->Feature_Parent );
4673 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4674 (LPBYTE)data,size);
4675 msi_free(data);
4678 /* the UI chunk */
4679 uirow = MSI_CreateRecord( 1 );
4680 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4681 msi_ui_actiondata( package, szPublishFeatures, uirow );
4682 msiobj_release( &uirow->hdr );
4683 /* FIXME: call msi_ui_progress? */
4686 end:
4687 RegCloseKey(hkey);
4688 RegCloseKey(userdata);
4689 return rc;
4692 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4694 UINT r;
4695 HKEY hkey;
4696 MSIRECORD *uirow;
4698 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4700 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4701 &hkey, FALSE);
4702 if (r == ERROR_SUCCESS)
4704 RegDeleteValueW(hkey, feature->Feature);
4705 RegCloseKey(hkey);
4708 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4709 &hkey, FALSE);
4710 if (r == ERROR_SUCCESS)
4712 RegDeleteValueW(hkey, feature->Feature);
4713 RegCloseKey(hkey);
4716 uirow = MSI_CreateRecord( 1 );
4717 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4718 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4719 msiobj_release( &uirow->hdr );
4721 return ERROR_SUCCESS;
4724 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4726 MSIFEATURE *feature;
4728 if (!msi_check_unpublish(package))
4729 return ERROR_SUCCESS;
4731 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4733 msi_unpublish_feature(package, feature);
4736 return ERROR_SUCCESS;
4739 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4741 SYSTEMTIME systime;
4742 DWORD size, langid;
4743 WCHAR date[9], *val, *buffer;
4744 const WCHAR *prop, *key;
4746 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4747 static const WCHAR modpath_fmt[] =
4748 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4749 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4750 static const WCHAR szModifyPath[] =
4751 {'M','o','d','i','f','y','P','a','t','h',0};
4752 static const WCHAR szUninstallString[] =
4753 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4754 static const WCHAR szEstimatedSize[] =
4755 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4756 static const WCHAR szDisplayVersion[] =
4757 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4758 static const WCHAR szInstallSource[] =
4759 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4760 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4761 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4762 static const WCHAR szAuthorizedCDFPrefix[] =
4763 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4764 static const WCHAR szARPCONTACT[] =
4765 {'A','R','P','C','O','N','T','A','C','T',0};
4766 static const WCHAR szContact[] =
4767 {'C','o','n','t','a','c','t',0};
4768 static const WCHAR szARPCOMMENTS[] =
4769 {'A','R','P','C','O','M','M','E','N','T','S',0};
4770 static const WCHAR szComments[] =
4771 {'C','o','m','m','e','n','t','s',0};
4772 static const WCHAR szProductName[] =
4773 {'P','r','o','d','u','c','t','N','a','m','e',0};
4774 static const WCHAR szDisplayName[] =
4775 {'D','i','s','p','l','a','y','N','a','m','e',0};
4776 static const WCHAR szARPHELPLINK[] =
4777 {'A','R','P','H','E','L','P','L','I','N','K',0};
4778 static const WCHAR szHelpLink[] =
4779 {'H','e','l','p','L','i','n','k',0};
4780 static const WCHAR szARPHELPTELEPHONE[] =
4781 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4782 static const WCHAR szHelpTelephone[] =
4783 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4784 static const WCHAR szARPINSTALLLOCATION[] =
4785 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4786 static const WCHAR szInstallLocation[] =
4787 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4788 static const WCHAR szManufacturer[] =
4789 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4790 static const WCHAR szPublisher[] =
4791 {'P','u','b','l','i','s','h','e','r',0};
4792 static const WCHAR szARPREADME[] =
4793 {'A','R','P','R','E','A','D','M','E',0};
4794 static const WCHAR szReadme[] =
4795 {'R','e','a','d','M','e',0};
4796 static const WCHAR szARPSIZE[] =
4797 {'A','R','P','S','I','Z','E',0};
4798 static const WCHAR szSize[] =
4799 {'S','i','z','e',0};
4800 static const WCHAR szARPURLINFOABOUT[] =
4801 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4802 static const WCHAR szURLInfoAbout[] =
4803 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4804 static const WCHAR szARPURLUPDATEINFO[] =
4805 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4806 static const WCHAR szURLUpdateInfo[] =
4807 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4808 static const WCHAR szARPSYSTEMCOMPONENT[] =
4809 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4810 static const WCHAR szSystemComponent[] =
4811 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4813 static const WCHAR *propval[] = {
4814 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4815 szARPCONTACT, szContact,
4816 szARPCOMMENTS, szComments,
4817 szProductName, szDisplayName,
4818 szARPHELPLINK, szHelpLink,
4819 szARPHELPTELEPHONE, szHelpTelephone,
4820 szARPINSTALLLOCATION, szInstallLocation,
4821 szSourceDir, szInstallSource,
4822 szManufacturer, szPublisher,
4823 szARPREADME, szReadme,
4824 szARPSIZE, szSize,
4825 szARPURLINFOABOUT, szURLInfoAbout,
4826 szARPURLUPDATEINFO, szURLUpdateInfo,
4827 NULL
4829 const WCHAR **p = propval;
4831 while (*p)
4833 prop = *p++;
4834 key = *p++;
4835 val = msi_dup_property(package->db, prop);
4836 msi_reg_set_val_str(hkey, key, val);
4837 msi_free(val);
4840 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4841 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4843 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4845 size = deformat_string(package, modpath_fmt, &buffer);
4846 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4847 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4848 msi_free(buffer);
4850 /* FIXME: Write real Estimated Size when we have it */
4851 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4853 GetLocalTime(&systime);
4854 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4855 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4857 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4858 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4860 buffer = msi_dup_property(package->db, szProductVersion);
4861 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4862 if (buffer)
4864 DWORD verdword = msi_version_str_to_dword(buffer);
4866 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4867 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4868 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4869 msi_free(buffer);
4872 return ERROR_SUCCESS;
4875 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4877 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4878 MSIRECORD *uirow;
4879 LPWSTR upgrade_code;
4880 HKEY hkey, props, upgrade_key;
4881 UINT rc;
4883 /* FIXME: also need to publish if the product is in advertise mode */
4884 if (!msi_check_publish(package))
4885 return ERROR_SUCCESS;
4887 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4888 if (rc != ERROR_SUCCESS)
4889 return rc;
4891 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4892 if (rc != ERROR_SUCCESS)
4893 goto done;
4895 rc = msi_publish_install_properties(package, hkey);
4896 if (rc != ERROR_SUCCESS)
4897 goto done;
4899 rc = msi_publish_install_properties(package, props);
4900 if (rc != ERROR_SUCCESS)
4901 goto done;
4903 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4904 if (upgrade_code)
4906 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4907 if (rc == ERROR_SUCCESS)
4909 squash_guid( package->ProductCode, squashed_pc );
4910 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4911 RegCloseKey( upgrade_key );
4913 msi_free( upgrade_code );
4915 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4916 package->delete_on_close = FALSE;
4918 done:
4919 uirow = MSI_CreateRecord( 1 );
4920 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4921 msi_ui_actiondata( package, szRegisterProduct, uirow );
4922 msiobj_release( &uirow->hdr );
4924 RegCloseKey(hkey);
4925 return ERROR_SUCCESS;
4928 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4930 return execute_script(package, SCRIPT_INSTALL);
4933 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4935 MSIPACKAGE *package = param;
4936 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4937 WCHAR *p, *icon_path;
4939 if (!icon) return ERROR_SUCCESS;
4940 if ((icon_path = msi_build_icon_path( package, icon )))
4942 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4943 DeleteFileW( icon_path );
4944 if ((p = strrchrW( icon_path, '\\' )))
4946 *p = 0;
4947 RemoveDirectoryW( icon_path );
4949 msi_free( icon_path );
4951 return ERROR_SUCCESS;
4954 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4956 static const WCHAR query[]= {
4957 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4958 MSIQUERY *view;
4959 UINT r;
4961 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4962 if (r == ERROR_SUCCESS)
4964 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4965 msiobj_release( &view->hdr );
4966 if (r != ERROR_SUCCESS)
4967 return r;
4969 return ERROR_SUCCESS;
4972 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4974 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4975 WCHAR *upgrade, **features;
4976 BOOL full_uninstall = TRUE;
4977 MSIFEATURE *feature;
4978 MSIPATCHINFO *patch;
4979 UINT i;
4981 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4983 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4985 features = msi_split_string( remove, ',' );
4986 for (i = 0; features && features[i]; i++)
4988 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4990 msi_free(features);
4992 if (!full_uninstall)
4993 return ERROR_SUCCESS;
4995 MSIREG_DeleteProductKey(package->ProductCode);
4996 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4997 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4999 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5000 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5001 MSIREG_DeleteUserProductKey(package->ProductCode);
5002 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5004 upgrade = msi_dup_property(package->db, szUpgradeCode);
5005 if (upgrade)
5007 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5008 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5009 msi_free(upgrade);
5012 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5014 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5015 if (!strcmpW( package->ProductCode, patch->products ))
5017 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5018 patch->delete_on_close = TRUE;
5020 /* FIXME: remove local patch package if this is the last product */
5022 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5023 package->delete_on_close = TRUE;
5025 msi_unpublish_icons( package );
5026 return ERROR_SUCCESS;
5029 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5031 UINT rc;
5032 WCHAR *remove;
5034 /* turn off scheduling */
5035 package->script->CurrentlyScripting= FALSE;
5037 /* first do the same as an InstallExecute */
5038 rc = ACTION_InstallExecute(package);
5039 if (rc != ERROR_SUCCESS)
5040 return rc;
5042 /* then handle commit actions */
5043 rc = execute_script(package, SCRIPT_COMMIT);
5044 if (rc != ERROR_SUCCESS)
5045 return rc;
5047 remove = msi_dup_property(package->db, szRemove);
5048 rc = msi_unpublish_product(package, remove);
5049 msi_free(remove);
5050 return rc;
5053 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5055 static const WCHAR RunOnce[] = {
5056 'S','o','f','t','w','a','r','e','\\',
5057 'M','i','c','r','o','s','o','f','t','\\',
5058 'W','i','n','d','o','w','s','\\',
5059 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5060 'R','u','n','O','n','c','e',0};
5061 static const WCHAR InstallRunOnce[] = {
5062 'S','o','f','t','w','a','r','e','\\',
5063 'M','i','c','r','o','s','o','f','t','\\',
5064 'W','i','n','d','o','w','s','\\',
5065 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5066 'I','n','s','t','a','l','l','e','r','\\',
5067 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5069 static const WCHAR msiexec_fmt[] = {
5070 '%','s',
5071 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5072 '\"','%','s','\"',0};
5073 static const WCHAR install_fmt[] = {
5074 '/','I',' ','\"','%','s','\"',' ',
5075 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5076 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5077 WCHAR buffer[256], sysdir[MAX_PATH];
5078 HKEY hkey;
5079 WCHAR squished_pc[100];
5081 squash_guid(package->ProductCode,squished_pc);
5083 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5084 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5085 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5086 squished_pc);
5088 msi_reg_set_val_str( hkey, squished_pc, buffer );
5089 RegCloseKey(hkey);
5091 TRACE("Reboot command %s\n",debugstr_w(buffer));
5093 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5094 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5096 msi_reg_set_val_str( hkey, squished_pc, buffer );
5097 RegCloseKey(hkey);
5099 return ERROR_INSTALL_SUSPEND;
5102 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5104 static const WCHAR query[] =
5105 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5106 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5107 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5108 MSIRECORD *rec, *row;
5109 DWORD i, size = 0;
5110 va_list va;
5111 const WCHAR *str;
5112 WCHAR *data;
5114 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5116 rec = MSI_CreateRecord( count + 2 );
5117 str = MSI_RecordGetString( row, 1 );
5118 MSI_RecordSetStringW( rec, 0, str );
5119 msiobj_release( &row->hdr );
5120 MSI_RecordSetInteger( rec, 1, error );
5122 va_start( va, count );
5123 for (i = 0; i < count; i++)
5125 str = va_arg( va, const WCHAR *);
5126 MSI_RecordSetStringW( rec, i + 2, str );
5128 va_end( va );
5130 MSI_FormatRecordW( package, rec, NULL, &size );
5131 size++;
5132 data = msi_alloc( size * sizeof(WCHAR) );
5133 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5134 else data[0] = 0;
5135 msiobj_release( &rec->hdr );
5136 return data;
5139 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5141 DWORD attrib;
5142 UINT rc;
5145 * We are currently doing what should be done here in the top level Install
5146 * however for Administrative and uninstalls this step will be needed
5148 if (!package->PackagePath)
5149 return ERROR_SUCCESS;
5151 msi_set_sourcedir_props(package, TRUE);
5153 attrib = GetFileAttributesW(package->db->path);
5154 if (attrib == INVALID_FILE_ATTRIBUTES)
5156 LPWSTR prompt, msg;
5157 DWORD size = 0;
5159 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5160 package->Context, MSICODE_PRODUCT,
5161 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5162 if (rc == ERROR_MORE_DATA)
5164 prompt = msi_alloc(size * sizeof(WCHAR));
5165 MsiSourceListGetInfoW(package->ProductCode, NULL,
5166 package->Context, MSICODE_PRODUCT,
5167 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5169 else
5170 prompt = strdupW(package->db->path);
5172 msg = msi_build_error_string(package, 1302, 1, prompt);
5173 msi_free(prompt);
5174 while(attrib == INVALID_FILE_ATTRIBUTES)
5176 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5177 if (rc == IDCANCEL)
5179 msi_free(msg);
5180 return ERROR_INSTALL_USEREXIT;
5182 attrib = GetFileAttributesW(package->db->path);
5184 msi_free(msg);
5185 rc = ERROR_SUCCESS;
5187 else
5188 return ERROR_SUCCESS;
5190 return rc;
5193 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5195 HKEY hkey = 0;
5196 LPWSTR buffer, productid = NULL;
5197 UINT i, rc = ERROR_SUCCESS;
5198 MSIRECORD *uirow;
5200 static const WCHAR szPropKeys[][80] =
5202 {'P','r','o','d','u','c','t','I','D',0},
5203 {'U','S','E','R','N','A','M','E',0},
5204 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5205 {0},
5208 static const WCHAR szRegKeys[][80] =
5210 {'P','r','o','d','u','c','t','I','D',0},
5211 {'R','e','g','O','w','n','e','r',0},
5212 {'R','e','g','C','o','m','p','a','n','y',0},
5213 {0},
5216 if (msi_check_unpublish(package))
5218 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5219 goto end;
5222 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5223 if (!productid)
5224 goto end;
5226 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5227 NULL, &hkey, TRUE);
5228 if (rc != ERROR_SUCCESS)
5229 goto end;
5231 for( i = 0; szPropKeys[i][0]; i++ )
5233 buffer = msi_dup_property( package->db, szPropKeys[i] );
5234 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5235 msi_free( buffer );
5238 end:
5239 uirow = MSI_CreateRecord( 1 );
5240 MSI_RecordSetStringW( uirow, 1, productid );
5241 msi_ui_actiondata( package, szRegisterUser, uirow );
5242 msiobj_release( &uirow->hdr );
5244 msi_free(productid);
5245 RegCloseKey(hkey);
5246 return rc;
5250 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5252 UINT rc;
5254 package->script->InWhatSequence |= SEQUENCE_EXEC;
5255 rc = ACTION_ProcessExecSequence(package,FALSE);
5256 return rc;
5259 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5261 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5262 WCHAR productid_85[21], component_85[21], *ret;
5263 GUID clsid;
5264 DWORD sz;
5266 /* > is used if there is a component GUID and < if not. */
5268 productid_85[0] = 0;
5269 component_85[0] = 0;
5270 CLSIDFromString( package->ProductCode, &clsid );
5272 encode_base85_guid( &clsid, productid_85 );
5273 if (component)
5275 CLSIDFromString( component->ComponentId, &clsid );
5276 encode_base85_guid( &clsid, component_85 );
5279 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5280 debugstr_w(component_85));
5282 sz = 20 + strlenW( feature ) + 20 + 3;
5283 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5284 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5285 return ret;
5288 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5290 MSIPACKAGE *package = param;
5291 LPCWSTR compgroupid, component, feature, qualifier, text;
5292 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5293 HKEY hkey = NULL;
5294 UINT rc;
5295 MSICOMPONENT *comp;
5296 MSIFEATURE *feat;
5297 DWORD sz;
5298 MSIRECORD *uirow;
5299 int len;
5301 feature = MSI_RecordGetString(rec, 5);
5302 feat = msi_get_loaded_feature(package, feature);
5303 if (!feat)
5304 return ERROR_SUCCESS;
5306 feat->Action = msi_get_feature_action( package, feat );
5307 if (feat->Action != INSTALLSTATE_LOCAL &&
5308 feat->Action != INSTALLSTATE_SOURCE &&
5309 feat->Action != INSTALLSTATE_ADVERTISED)
5311 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5312 return ERROR_SUCCESS;
5315 component = MSI_RecordGetString(rec, 3);
5316 comp = msi_get_loaded_component(package, component);
5317 if (!comp)
5318 return ERROR_SUCCESS;
5320 compgroupid = MSI_RecordGetString(rec,1);
5321 qualifier = MSI_RecordGetString(rec,2);
5323 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5324 if (rc != ERROR_SUCCESS)
5325 goto end;
5327 advertise = msi_create_component_advertise_string( package, comp, feature );
5328 text = MSI_RecordGetString( rec, 4 );
5329 if (text)
5331 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5332 strcpyW( p, advertise );
5333 strcatW( p, text );
5334 msi_free( advertise );
5335 advertise = p;
5337 existing = msi_reg_get_val_str( hkey, qualifier );
5339 sz = strlenW( advertise ) + 1;
5340 if (existing)
5342 for (p = existing; *p; p += len)
5344 len = strlenW( p ) + 1;
5345 if (strcmpW( advertise, p )) sz += len;
5348 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5350 rc = ERROR_OUTOFMEMORY;
5351 goto end;
5353 q = output;
5354 if (existing)
5356 for (p = existing; *p; p += len)
5358 len = strlenW( p ) + 1;
5359 if (strcmpW( advertise, p ))
5361 memcpy( q, p, len * sizeof(WCHAR) );
5362 q += len;
5366 strcpyW( q, advertise );
5367 q[strlenW( q ) + 1] = 0;
5369 msi_reg_set_val_multi_str( hkey, qualifier, output );
5371 end:
5372 RegCloseKey(hkey);
5373 msi_free( output );
5374 msi_free( advertise );
5375 msi_free( existing );
5377 /* the UI chunk */
5378 uirow = MSI_CreateRecord( 2 );
5379 MSI_RecordSetStringW( uirow, 1, compgroupid );
5380 MSI_RecordSetStringW( uirow, 2, qualifier);
5381 msi_ui_actiondata( package, szPublishComponents, uirow );
5382 msiobj_release( &uirow->hdr );
5383 /* FIXME: call ui_progress? */
5385 return rc;
5389 * At present I am ignorning the advertised components part of this and only
5390 * focusing on the qualified component sets
5392 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5394 static const WCHAR query[] = {
5395 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5396 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5397 MSIQUERY *view;
5398 UINT rc;
5400 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5401 if (rc != ERROR_SUCCESS)
5402 return ERROR_SUCCESS;
5404 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5405 msiobj_release(&view->hdr);
5406 return rc;
5409 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5411 static const WCHAR szInstallerComponents[] = {
5412 'S','o','f','t','w','a','r','e','\\',
5413 'M','i','c','r','o','s','o','f','t','\\',
5414 'I','n','s','t','a','l','l','e','r','\\',
5415 'C','o','m','p','o','n','e','n','t','s','\\',0};
5417 MSIPACKAGE *package = param;
5418 LPCWSTR compgroupid, component, feature, qualifier;
5419 MSICOMPONENT *comp;
5420 MSIFEATURE *feat;
5421 MSIRECORD *uirow;
5422 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5423 LONG res;
5425 feature = MSI_RecordGetString( rec, 5 );
5426 feat = msi_get_loaded_feature( package, feature );
5427 if (!feat)
5428 return ERROR_SUCCESS;
5430 feat->Action = msi_get_feature_action( package, feat );
5431 if (feat->Action != INSTALLSTATE_ABSENT)
5433 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5434 return ERROR_SUCCESS;
5437 component = MSI_RecordGetString( rec, 3 );
5438 comp = msi_get_loaded_component( package, component );
5439 if (!comp)
5440 return ERROR_SUCCESS;
5442 compgroupid = MSI_RecordGetString( rec, 1 );
5443 qualifier = MSI_RecordGetString( rec, 2 );
5445 squash_guid( compgroupid, squashed );
5446 strcpyW( keypath, szInstallerComponents );
5447 strcatW( keypath, squashed );
5449 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5450 if (res != ERROR_SUCCESS)
5452 WARN("Unable to delete component key %d\n", res);
5455 uirow = MSI_CreateRecord( 2 );
5456 MSI_RecordSetStringW( uirow, 1, compgroupid );
5457 MSI_RecordSetStringW( uirow, 2, qualifier );
5458 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5459 msiobj_release( &uirow->hdr );
5461 return ERROR_SUCCESS;
5464 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5466 static const WCHAR query[] = {
5467 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5468 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5469 MSIQUERY *view;
5470 UINT rc;
5472 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5473 if (rc != ERROR_SUCCESS)
5474 return ERROR_SUCCESS;
5476 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5477 msiobj_release( &view->hdr );
5478 return rc;
5481 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5483 static const WCHAR query[] =
5484 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5485 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5486 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5487 MSIPACKAGE *package = param;
5488 MSICOMPONENT *component;
5489 MSIRECORD *row;
5490 MSIFILE *file;
5491 SC_HANDLE hscm = NULL, service = NULL;
5492 LPCWSTR comp, key;
5493 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5494 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5495 DWORD serv_type, start_type, err_control;
5496 SERVICE_DESCRIPTIONW sd = {NULL};
5498 comp = MSI_RecordGetString( rec, 12 );
5499 component = msi_get_loaded_component( package, comp );
5500 if (!component)
5502 WARN("service component not found\n");
5503 goto done;
5505 component->Action = msi_get_component_action( package, component );
5506 if (component->Action != INSTALLSTATE_LOCAL)
5508 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5509 goto done;
5511 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5512 if (!hscm)
5514 ERR("Failed to open the SC Manager!\n");
5515 goto done;
5518 start_type = MSI_RecordGetInteger(rec, 5);
5519 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5520 goto done;
5522 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5523 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5524 serv_type = MSI_RecordGetInteger(rec, 4);
5525 err_control = MSI_RecordGetInteger(rec, 6);
5526 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5527 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5528 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5529 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5530 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5531 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5533 /* fetch the service path */
5534 row = MSI_QueryGetRecord(package->db, query, comp);
5535 if (!row)
5537 ERR("Query failed\n");
5538 goto done;
5540 key = MSI_RecordGetString(row, 6);
5541 file = msi_get_loaded_file(package, key);
5542 msiobj_release(&row->hdr);
5543 if (!file)
5545 ERR("Failed to load the service file\n");
5546 goto done;
5549 if (!args || !args[0]) image_path = file->TargetPath;
5550 else
5552 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5553 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5554 return ERROR_OUTOFMEMORY;
5556 strcpyW(image_path, file->TargetPath);
5557 strcatW(image_path, szSpace);
5558 strcatW(image_path, args);
5560 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5561 start_type, err_control, image_path, load_order,
5562 NULL, depends, serv_name, pass);
5564 if (!service)
5566 if (GetLastError() != ERROR_SERVICE_EXISTS)
5567 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5569 else if (sd.lpDescription)
5571 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5572 WARN("failed to set service description %u\n", GetLastError());
5575 if (image_path != file->TargetPath) msi_free(image_path);
5576 done:
5577 CloseServiceHandle(service);
5578 CloseServiceHandle(hscm);
5579 msi_free(name);
5580 msi_free(disp);
5581 msi_free(sd.lpDescription);
5582 msi_free(load_order);
5583 msi_free(serv_name);
5584 msi_free(pass);
5585 msi_free(depends);
5586 msi_free(args);
5588 return ERROR_SUCCESS;
5591 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5593 static const WCHAR query[] = {
5594 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5595 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5596 MSIQUERY *view;
5597 UINT rc;
5599 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5600 if (rc != ERROR_SUCCESS)
5601 return ERROR_SUCCESS;
5603 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5604 msiobj_release(&view->hdr);
5605 return rc;
5608 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5609 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5611 LPCWSTR *vector, *temp_vector;
5612 LPWSTR p, q;
5613 DWORD sep_len;
5615 static const WCHAR separator[] = {'[','~',']',0};
5617 *numargs = 0;
5618 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5620 if (!args)
5621 return NULL;
5623 vector = msi_alloc(sizeof(LPWSTR));
5624 if (!vector)
5625 return NULL;
5627 p = args;
5630 (*numargs)++;
5631 vector[*numargs - 1] = p;
5633 if ((q = strstrW(p, separator)))
5635 *q = '\0';
5637 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5638 if (!temp_vector)
5640 msi_free(vector);
5641 return NULL;
5643 vector = temp_vector;
5645 p = q + sep_len;
5647 } while (q);
5649 return vector;
5652 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5654 MSIPACKAGE *package = param;
5655 MSICOMPONENT *comp;
5656 MSIRECORD *uirow;
5657 SC_HANDLE scm = NULL, service = NULL;
5658 LPCWSTR component, *vector = NULL;
5659 LPWSTR name, args, display_name = NULL;
5660 DWORD event, numargs, len, wait, dummy;
5661 UINT r = ERROR_FUNCTION_FAILED;
5662 SERVICE_STATUS_PROCESS status;
5663 ULONGLONG start_time;
5665 component = MSI_RecordGetString(rec, 6);
5666 comp = msi_get_loaded_component(package, component);
5667 if (!comp)
5668 return ERROR_SUCCESS;
5670 comp->Action = msi_get_component_action( package, comp );
5671 if (comp->Action != INSTALLSTATE_LOCAL)
5673 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5674 return ERROR_SUCCESS;
5677 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5678 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5679 event = MSI_RecordGetInteger(rec, 3);
5680 wait = MSI_RecordGetInteger(rec, 5);
5682 if (!(event & msidbServiceControlEventStart))
5684 r = ERROR_SUCCESS;
5685 goto done;
5688 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5689 if (!scm)
5691 ERR("Failed to open the service control manager\n");
5692 goto done;
5695 len = 0;
5696 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5697 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5699 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5700 GetServiceDisplayNameW( scm, name, display_name, &len );
5703 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5704 if (!service)
5706 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5707 goto done;
5710 vector = msi_service_args_to_vector(args, &numargs);
5712 if (!StartServiceW(service, numargs, vector) &&
5713 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5715 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5716 goto done;
5719 r = ERROR_SUCCESS;
5720 if (wait)
5722 /* wait for at most 30 seconds for the service to be up and running */
5723 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5724 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5726 TRACE("failed to query service status (%u)\n", GetLastError());
5727 goto done;
5729 start_time = GetTickCount64();
5730 while (status.dwCurrentState == SERVICE_START_PENDING)
5732 if (GetTickCount64() - start_time > 30000) break;
5733 Sleep(1000);
5734 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5735 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5737 TRACE("failed to query service status (%u)\n", GetLastError());
5738 goto done;
5741 if (status.dwCurrentState != SERVICE_RUNNING)
5743 WARN("service failed to start %u\n", status.dwCurrentState);
5744 r = ERROR_FUNCTION_FAILED;
5748 done:
5749 uirow = MSI_CreateRecord( 2 );
5750 MSI_RecordSetStringW( uirow, 1, display_name );
5751 MSI_RecordSetStringW( uirow, 2, name );
5752 msi_ui_actiondata( package, szStartServices, uirow );
5753 msiobj_release( &uirow->hdr );
5755 CloseServiceHandle(service);
5756 CloseServiceHandle(scm);
5758 msi_free(name);
5759 msi_free(args);
5760 msi_free(vector);
5761 msi_free(display_name);
5762 return r;
5765 static UINT ACTION_StartServices( MSIPACKAGE *package )
5767 static const WCHAR query[] = {
5768 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5769 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5770 MSIQUERY *view;
5771 UINT rc;
5773 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5774 if (rc != ERROR_SUCCESS)
5775 return ERROR_SUCCESS;
5777 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5778 msiobj_release(&view->hdr);
5779 return rc;
5782 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5784 DWORD i, needed, count;
5785 ENUM_SERVICE_STATUSW *dependencies;
5786 SERVICE_STATUS ss;
5787 SC_HANDLE depserv;
5789 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5790 0, &needed, &count))
5791 return TRUE;
5793 if (GetLastError() != ERROR_MORE_DATA)
5794 return FALSE;
5796 dependencies = msi_alloc(needed);
5797 if (!dependencies)
5798 return FALSE;
5800 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5801 needed, &needed, &count))
5802 goto error;
5804 for (i = 0; i < count; i++)
5806 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5807 SERVICE_STOP | SERVICE_QUERY_STATUS);
5808 if (!depserv)
5809 goto error;
5811 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5812 goto error;
5815 return TRUE;
5817 error:
5818 msi_free(dependencies);
5819 return FALSE;
5822 static UINT stop_service( LPCWSTR name )
5824 SC_HANDLE scm = NULL, service = NULL;
5825 SERVICE_STATUS status;
5826 SERVICE_STATUS_PROCESS ssp;
5827 DWORD needed;
5829 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5830 if (!scm)
5832 WARN("Failed to open the SCM: %d\n", GetLastError());
5833 goto done;
5836 service = OpenServiceW(scm, name,
5837 SERVICE_STOP |
5838 SERVICE_QUERY_STATUS |
5839 SERVICE_ENUMERATE_DEPENDENTS);
5840 if (!service)
5842 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5843 goto done;
5846 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5847 sizeof(SERVICE_STATUS_PROCESS), &needed))
5849 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5850 goto done;
5853 if (ssp.dwCurrentState == SERVICE_STOPPED)
5854 goto done;
5856 stop_service_dependents(scm, service);
5858 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5859 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5861 done:
5862 CloseServiceHandle(service);
5863 CloseServiceHandle(scm);
5865 return ERROR_SUCCESS;
5868 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5870 MSIPACKAGE *package = param;
5871 MSICOMPONENT *comp;
5872 MSIRECORD *uirow;
5873 LPCWSTR component;
5874 LPWSTR name = NULL, display_name = NULL;
5875 DWORD event, len;
5876 SC_HANDLE scm;
5878 event = MSI_RecordGetInteger( rec, 3 );
5879 if (!(event & msidbServiceControlEventStop))
5880 return ERROR_SUCCESS;
5882 component = MSI_RecordGetString( rec, 6 );
5883 comp = msi_get_loaded_component( package, component );
5884 if (!comp)
5885 return ERROR_SUCCESS;
5887 comp->Action = msi_get_component_action( package, comp );
5888 if (comp->Action != INSTALLSTATE_ABSENT)
5890 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5891 return ERROR_SUCCESS;
5894 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5895 if (!scm)
5897 ERR("Failed to open the service control manager\n");
5898 goto done;
5901 len = 0;
5902 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5903 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5905 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5906 GetServiceDisplayNameW( scm, name, display_name, &len );
5908 CloseServiceHandle( scm );
5910 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5911 stop_service( name );
5913 done:
5914 uirow = MSI_CreateRecord( 2 );
5915 MSI_RecordSetStringW( uirow, 1, display_name );
5916 MSI_RecordSetStringW( uirow, 2, name );
5917 msi_ui_actiondata( package, szStopServices, uirow );
5918 msiobj_release( &uirow->hdr );
5920 msi_free( name );
5921 msi_free( display_name );
5922 return ERROR_SUCCESS;
5925 static UINT ACTION_StopServices( MSIPACKAGE *package )
5927 static const WCHAR query[] = {
5928 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5929 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5930 MSIQUERY *view;
5931 UINT rc;
5933 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5934 if (rc != ERROR_SUCCESS)
5935 return ERROR_SUCCESS;
5937 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5938 msiobj_release(&view->hdr);
5939 return rc;
5942 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5944 MSIPACKAGE *package = param;
5945 MSICOMPONENT *comp;
5946 MSIRECORD *uirow;
5947 LPWSTR name = NULL, display_name = NULL;
5948 DWORD event, len;
5949 SC_HANDLE scm = NULL, service = NULL;
5951 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
5952 if (!comp)
5953 return ERROR_SUCCESS;
5955 event = MSI_RecordGetInteger( rec, 3 );
5956 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5958 comp->Action = msi_get_component_action( package, comp );
5959 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
5960 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
5962 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
5963 msi_free( name );
5964 return ERROR_SUCCESS;
5966 stop_service( name );
5968 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5969 if (!scm)
5971 WARN("Failed to open the SCM: %d\n", GetLastError());
5972 goto done;
5975 len = 0;
5976 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5977 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5979 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5980 GetServiceDisplayNameW( scm, name, display_name, &len );
5983 service = OpenServiceW( scm, name, DELETE );
5984 if (!service)
5986 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5987 goto done;
5990 if (!DeleteService( service ))
5991 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5993 done:
5994 uirow = MSI_CreateRecord( 2 );
5995 MSI_RecordSetStringW( uirow, 1, display_name );
5996 MSI_RecordSetStringW( uirow, 2, name );
5997 msi_ui_actiondata( package, szDeleteServices, uirow );
5998 msiobj_release( &uirow->hdr );
6000 CloseServiceHandle( service );
6001 CloseServiceHandle( scm );
6002 msi_free( name );
6003 msi_free( display_name );
6005 return ERROR_SUCCESS;
6008 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6010 static const WCHAR query[] = {
6011 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6012 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6013 MSIQUERY *view;
6014 UINT rc;
6016 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6017 if (rc != ERROR_SUCCESS)
6018 return ERROR_SUCCESS;
6020 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6021 msiobj_release( &view->hdr );
6022 return rc;
6025 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6027 MSIPACKAGE *package = param;
6028 LPWSTR driver, driver_path, ptr;
6029 WCHAR outpath[MAX_PATH];
6030 MSIFILE *driver_file = NULL, *setup_file = NULL;
6031 MSICOMPONENT *comp;
6032 MSIRECORD *uirow;
6033 LPCWSTR desc, file_key, component;
6034 DWORD len, usage;
6035 UINT r = ERROR_SUCCESS;
6037 static const WCHAR driver_fmt[] = {
6038 'D','r','i','v','e','r','=','%','s',0};
6039 static const WCHAR setup_fmt[] = {
6040 'S','e','t','u','p','=','%','s',0};
6041 static const WCHAR usage_fmt[] = {
6042 'F','i','l','e','U','s','a','g','e','=','1',0};
6044 component = MSI_RecordGetString( rec, 2 );
6045 comp = msi_get_loaded_component( package, component );
6046 if (!comp)
6047 return ERROR_SUCCESS;
6049 comp->Action = msi_get_component_action( package, comp );
6050 if (comp->Action != INSTALLSTATE_LOCAL)
6052 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6053 return ERROR_SUCCESS;
6055 desc = MSI_RecordGetString(rec, 3);
6057 file_key = MSI_RecordGetString( rec, 4 );
6058 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6060 file_key = MSI_RecordGetString( rec, 5 );
6061 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6063 if (!driver_file)
6065 ERR("ODBC Driver entry not found!\n");
6066 return ERROR_FUNCTION_FAILED;
6069 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6070 if (setup_file)
6071 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6072 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6074 driver = msi_alloc(len * sizeof(WCHAR));
6075 if (!driver)
6076 return ERROR_OUTOFMEMORY;
6078 ptr = driver;
6079 lstrcpyW(ptr, desc);
6080 ptr += lstrlenW(ptr) + 1;
6082 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6083 ptr += len + 1;
6085 if (setup_file)
6087 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6088 ptr += len + 1;
6091 lstrcpyW(ptr, usage_fmt);
6092 ptr += lstrlenW(ptr) + 1;
6093 *ptr = '\0';
6095 if (!driver_file->TargetPath)
6097 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6098 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6100 driver_path = strdupW(driver_file->TargetPath);
6101 ptr = strrchrW(driver_path, '\\');
6102 if (ptr) *ptr = '\0';
6104 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6105 NULL, ODBC_INSTALL_COMPLETE, &usage))
6107 ERR("Failed to install SQL driver!\n");
6108 r = ERROR_FUNCTION_FAILED;
6111 uirow = MSI_CreateRecord( 5 );
6112 MSI_RecordSetStringW( uirow, 1, desc );
6113 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6114 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6115 msi_ui_actiondata( package, szInstallODBC, uirow );
6116 msiobj_release( &uirow->hdr );
6118 msi_free(driver);
6119 msi_free(driver_path);
6121 return r;
6124 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6126 MSIPACKAGE *package = param;
6127 LPWSTR translator, translator_path, ptr;
6128 WCHAR outpath[MAX_PATH];
6129 MSIFILE *translator_file = NULL, *setup_file = NULL;
6130 MSICOMPONENT *comp;
6131 MSIRECORD *uirow;
6132 LPCWSTR desc, file_key, component;
6133 DWORD len, usage;
6134 UINT r = ERROR_SUCCESS;
6136 static const WCHAR translator_fmt[] = {
6137 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6138 static const WCHAR setup_fmt[] = {
6139 'S','e','t','u','p','=','%','s',0};
6141 component = MSI_RecordGetString( rec, 2 );
6142 comp = msi_get_loaded_component( package, component );
6143 if (!comp)
6144 return ERROR_SUCCESS;
6146 comp->Action = msi_get_component_action( package, comp );
6147 if (comp->Action != INSTALLSTATE_LOCAL)
6149 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6150 return ERROR_SUCCESS;
6152 desc = MSI_RecordGetString(rec, 3);
6154 file_key = MSI_RecordGetString( rec, 4 );
6155 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6157 file_key = MSI_RecordGetString( rec, 5 );
6158 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6160 if (!translator_file)
6162 ERR("ODBC Translator entry not found!\n");
6163 return ERROR_FUNCTION_FAILED;
6166 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6167 if (setup_file)
6168 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6170 translator = msi_alloc(len * sizeof(WCHAR));
6171 if (!translator)
6172 return ERROR_OUTOFMEMORY;
6174 ptr = translator;
6175 lstrcpyW(ptr, desc);
6176 ptr += lstrlenW(ptr) + 1;
6178 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6179 ptr += len + 1;
6181 if (setup_file)
6183 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6184 ptr += len + 1;
6186 *ptr = '\0';
6188 translator_path = strdupW(translator_file->TargetPath);
6189 ptr = strrchrW(translator_path, '\\');
6190 if (ptr) *ptr = '\0';
6192 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6193 NULL, ODBC_INSTALL_COMPLETE, &usage))
6195 ERR("Failed to install SQL translator!\n");
6196 r = ERROR_FUNCTION_FAILED;
6199 uirow = MSI_CreateRecord( 5 );
6200 MSI_RecordSetStringW( uirow, 1, desc );
6201 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6202 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6203 msi_ui_actiondata( package, szInstallODBC, uirow );
6204 msiobj_release( &uirow->hdr );
6206 msi_free(translator);
6207 msi_free(translator_path);
6209 return r;
6212 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6214 MSIPACKAGE *package = param;
6215 MSICOMPONENT *comp;
6216 LPWSTR attrs;
6217 LPCWSTR desc, driver, component;
6218 WORD request = ODBC_ADD_SYS_DSN;
6219 INT registration;
6220 DWORD len;
6221 UINT r = ERROR_SUCCESS;
6222 MSIRECORD *uirow;
6224 static const WCHAR attrs_fmt[] = {
6225 'D','S','N','=','%','s',0 };
6227 component = MSI_RecordGetString( rec, 2 );
6228 comp = msi_get_loaded_component( package, component );
6229 if (!comp)
6230 return ERROR_SUCCESS;
6232 comp->Action = msi_get_component_action( package, comp );
6233 if (comp->Action != INSTALLSTATE_LOCAL)
6235 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6236 return ERROR_SUCCESS;
6239 desc = MSI_RecordGetString(rec, 3);
6240 driver = MSI_RecordGetString(rec, 4);
6241 registration = MSI_RecordGetInteger(rec, 5);
6243 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6244 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6246 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6247 attrs = msi_alloc(len * sizeof(WCHAR));
6248 if (!attrs)
6249 return ERROR_OUTOFMEMORY;
6251 len = sprintfW(attrs, attrs_fmt, desc);
6252 attrs[len + 1] = 0;
6254 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6256 ERR("Failed to install SQL data source!\n");
6257 r = ERROR_FUNCTION_FAILED;
6260 uirow = MSI_CreateRecord( 5 );
6261 MSI_RecordSetStringW( uirow, 1, desc );
6262 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6263 MSI_RecordSetInteger( uirow, 3, request );
6264 msi_ui_actiondata( package, szInstallODBC, uirow );
6265 msiobj_release( &uirow->hdr );
6267 msi_free(attrs);
6269 return r;
6272 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6274 static const WCHAR driver_query[] = {
6275 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6276 'O','D','B','C','D','r','i','v','e','r',0};
6277 static const WCHAR translator_query[] = {
6278 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6279 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6280 static const WCHAR source_query[] = {
6281 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6282 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6283 MSIQUERY *view;
6284 UINT rc;
6286 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6287 if (rc == ERROR_SUCCESS)
6289 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6290 msiobj_release(&view->hdr);
6291 if (rc != ERROR_SUCCESS)
6292 return rc;
6294 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6295 if (rc == ERROR_SUCCESS)
6297 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6298 msiobj_release(&view->hdr);
6299 if (rc != ERROR_SUCCESS)
6300 return rc;
6302 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6303 if (rc == ERROR_SUCCESS)
6305 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6306 msiobj_release(&view->hdr);
6307 if (rc != ERROR_SUCCESS)
6308 return rc;
6310 return ERROR_SUCCESS;
6313 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6315 MSIPACKAGE *package = param;
6316 MSICOMPONENT *comp;
6317 MSIRECORD *uirow;
6318 DWORD usage;
6319 LPCWSTR desc, component;
6321 component = MSI_RecordGetString( rec, 2 );
6322 comp = msi_get_loaded_component( package, component );
6323 if (!comp)
6324 return ERROR_SUCCESS;
6326 comp->Action = msi_get_component_action( package, comp );
6327 if (comp->Action != INSTALLSTATE_ABSENT)
6329 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6330 return ERROR_SUCCESS;
6333 desc = MSI_RecordGetString( rec, 3 );
6334 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6336 WARN("Failed to remove ODBC driver\n");
6338 else if (!usage)
6340 FIXME("Usage count reached 0\n");
6343 uirow = MSI_CreateRecord( 2 );
6344 MSI_RecordSetStringW( uirow, 1, desc );
6345 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6346 msi_ui_actiondata( package, szRemoveODBC, uirow );
6347 msiobj_release( &uirow->hdr );
6349 return ERROR_SUCCESS;
6352 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6354 MSIPACKAGE *package = param;
6355 MSICOMPONENT *comp;
6356 MSIRECORD *uirow;
6357 DWORD usage;
6358 LPCWSTR desc, component;
6360 component = MSI_RecordGetString( rec, 2 );
6361 comp = msi_get_loaded_component( package, component );
6362 if (!comp)
6363 return ERROR_SUCCESS;
6365 comp->Action = msi_get_component_action( package, comp );
6366 if (comp->Action != INSTALLSTATE_ABSENT)
6368 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6369 return ERROR_SUCCESS;
6372 desc = MSI_RecordGetString( rec, 3 );
6373 if (!SQLRemoveTranslatorW( desc, &usage ))
6375 WARN("Failed to remove ODBC translator\n");
6377 else if (!usage)
6379 FIXME("Usage count reached 0\n");
6382 uirow = MSI_CreateRecord( 2 );
6383 MSI_RecordSetStringW( uirow, 1, desc );
6384 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6385 msi_ui_actiondata( package, szRemoveODBC, uirow );
6386 msiobj_release( &uirow->hdr );
6388 return ERROR_SUCCESS;
6391 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6393 MSIPACKAGE *package = param;
6394 MSICOMPONENT *comp;
6395 MSIRECORD *uirow;
6396 LPWSTR attrs;
6397 LPCWSTR desc, driver, component;
6398 WORD request = ODBC_REMOVE_SYS_DSN;
6399 INT registration;
6400 DWORD len;
6402 static const WCHAR attrs_fmt[] = {
6403 'D','S','N','=','%','s',0 };
6405 component = MSI_RecordGetString( rec, 2 );
6406 comp = msi_get_loaded_component( package, component );
6407 if (!comp)
6408 return ERROR_SUCCESS;
6410 comp->Action = msi_get_component_action( package, comp );
6411 if (comp->Action != INSTALLSTATE_ABSENT)
6413 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6414 return ERROR_SUCCESS;
6417 desc = MSI_RecordGetString( rec, 3 );
6418 driver = MSI_RecordGetString( rec, 4 );
6419 registration = MSI_RecordGetInteger( rec, 5 );
6421 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6422 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6424 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6425 attrs = msi_alloc( len * sizeof(WCHAR) );
6426 if (!attrs)
6427 return ERROR_OUTOFMEMORY;
6429 FIXME("Use ODBCSourceAttribute table\n");
6431 len = sprintfW( attrs, attrs_fmt, desc );
6432 attrs[len + 1] = 0;
6434 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6436 WARN("Failed to remove ODBC data source\n");
6438 msi_free( attrs );
6440 uirow = MSI_CreateRecord( 3 );
6441 MSI_RecordSetStringW( uirow, 1, desc );
6442 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6443 MSI_RecordSetInteger( uirow, 3, request );
6444 msi_ui_actiondata( package, szRemoveODBC, uirow );
6445 msiobj_release( &uirow->hdr );
6447 return ERROR_SUCCESS;
6450 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6452 static const WCHAR driver_query[] = {
6453 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6454 'O','D','B','C','D','r','i','v','e','r',0};
6455 static const WCHAR translator_query[] = {
6456 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6457 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6458 static const WCHAR source_query[] = {
6459 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6460 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6461 MSIQUERY *view;
6462 UINT rc;
6464 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6465 if (rc == ERROR_SUCCESS)
6467 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6468 msiobj_release( &view->hdr );
6469 if (rc != ERROR_SUCCESS)
6470 return rc;
6472 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6473 if (rc == ERROR_SUCCESS)
6475 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6476 msiobj_release( &view->hdr );
6477 if (rc != ERROR_SUCCESS)
6478 return rc;
6480 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6481 if (rc == ERROR_SUCCESS)
6483 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6484 msiobj_release( &view->hdr );
6485 if (rc != ERROR_SUCCESS)
6486 return rc;
6488 return ERROR_SUCCESS;
6491 #define ENV_ACT_SETALWAYS 0x1
6492 #define ENV_ACT_SETABSENT 0x2
6493 #define ENV_ACT_REMOVE 0x4
6494 #define ENV_ACT_REMOVEMATCH 0x8
6496 #define ENV_MOD_MACHINE 0x20000000
6497 #define ENV_MOD_APPEND 0x40000000
6498 #define ENV_MOD_PREFIX 0x80000000
6499 #define ENV_MOD_MASK 0xC0000000
6501 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6503 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6505 LPCWSTR cptr = *name;
6507 static const WCHAR prefix[] = {'[','~',']',0};
6508 static const int prefix_len = 3;
6510 *flags = 0;
6511 while (*cptr)
6513 if (*cptr == '=')
6514 *flags |= ENV_ACT_SETALWAYS;
6515 else if (*cptr == '+')
6516 *flags |= ENV_ACT_SETABSENT;
6517 else if (*cptr == '-')
6518 *flags |= ENV_ACT_REMOVE;
6519 else if (*cptr == '!')
6520 *flags |= ENV_ACT_REMOVEMATCH;
6521 else if (*cptr == '*')
6522 *flags |= ENV_MOD_MACHINE;
6523 else
6524 break;
6526 cptr++;
6527 (*name)++;
6530 if (!*cptr)
6532 ERR("Missing environment variable\n");
6533 return ERROR_FUNCTION_FAILED;
6536 if (*value)
6538 LPCWSTR ptr = *value;
6539 if (!strncmpW(ptr, prefix, prefix_len))
6541 if (ptr[prefix_len] == szSemiColon[0])
6543 *flags |= ENV_MOD_APPEND;
6544 *value += lstrlenW(prefix);
6546 else
6548 *value = NULL;
6551 else if (lstrlenW(*value) >= prefix_len)
6553 ptr += lstrlenW(ptr) - prefix_len;
6554 if (!strcmpW( ptr, prefix ))
6556 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6558 *flags |= ENV_MOD_PREFIX;
6559 /* the "[~]" will be removed by deformat_string */;
6561 else
6563 *value = NULL;
6569 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6570 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6571 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6572 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6574 ERR("Invalid flags: %08x\n", *flags);
6575 return ERROR_FUNCTION_FAILED;
6578 if (!*flags)
6579 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6581 return ERROR_SUCCESS;
6584 static UINT open_env_key( DWORD flags, HKEY *key )
6586 static const WCHAR user_env[] =
6587 {'E','n','v','i','r','o','n','m','e','n','t',0};
6588 static const WCHAR machine_env[] =
6589 {'S','y','s','t','e','m','\\',
6590 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6591 'C','o','n','t','r','o','l','\\',
6592 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6593 'E','n','v','i','r','o','n','m','e','n','t',0};
6594 const WCHAR *env;
6595 HKEY root;
6596 LONG res;
6598 if (flags & ENV_MOD_MACHINE)
6600 env = machine_env;
6601 root = HKEY_LOCAL_MACHINE;
6603 else
6605 env = user_env;
6606 root = HKEY_CURRENT_USER;
6609 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6610 if (res != ERROR_SUCCESS)
6612 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6613 return ERROR_FUNCTION_FAILED;
6616 return ERROR_SUCCESS;
6619 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6621 MSIPACKAGE *package = param;
6622 LPCWSTR name, value, component;
6623 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6624 DWORD flags, type, size;
6625 UINT res;
6626 HKEY env = NULL;
6627 MSICOMPONENT *comp;
6628 MSIRECORD *uirow;
6629 int action = 0;
6631 component = MSI_RecordGetString(rec, 4);
6632 comp = msi_get_loaded_component(package, component);
6633 if (!comp)
6634 return ERROR_SUCCESS;
6636 comp->Action = msi_get_component_action( package, comp );
6637 if (comp->Action != INSTALLSTATE_LOCAL)
6639 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6640 return ERROR_SUCCESS;
6642 name = MSI_RecordGetString(rec, 2);
6643 value = MSI_RecordGetString(rec, 3);
6645 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6647 res = env_parse_flags(&name, &value, &flags);
6648 if (res != ERROR_SUCCESS || !value)
6649 goto done;
6651 if (value && !deformat_string(package, value, &deformatted))
6653 res = ERROR_OUTOFMEMORY;
6654 goto done;
6657 value = deformatted;
6659 res = open_env_key( flags, &env );
6660 if (res != ERROR_SUCCESS)
6661 goto done;
6663 if (flags & ENV_MOD_MACHINE)
6664 action |= 0x20000000;
6666 size = 0;
6667 type = REG_SZ;
6668 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6669 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6670 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6671 goto done;
6673 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6675 action = 0x2;
6677 /* Nothing to do. */
6678 if (!value)
6680 res = ERROR_SUCCESS;
6681 goto done;
6684 /* If we are appending but the string was empty, strip ; */
6685 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6687 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6688 newval = strdupW(value);
6689 if (!newval)
6691 res = ERROR_OUTOFMEMORY;
6692 goto done;
6695 else
6697 action = 0x1;
6699 /* Contrary to MSDN, +-variable to [~];path works */
6700 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6702 res = ERROR_SUCCESS;
6703 goto done;
6706 data = msi_alloc(size);
6707 if (!data)
6709 RegCloseKey(env);
6710 return ERROR_OUTOFMEMORY;
6713 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6714 if (res != ERROR_SUCCESS)
6715 goto done;
6717 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6719 action = 0x4;
6720 res = RegDeleteValueW(env, name);
6721 if (res != ERROR_SUCCESS)
6722 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6723 goto done;
6726 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6727 if (flags & ENV_MOD_MASK)
6729 DWORD mod_size;
6730 int multiplier = 0;
6731 if (flags & ENV_MOD_APPEND) multiplier++;
6732 if (flags & ENV_MOD_PREFIX) multiplier++;
6733 mod_size = lstrlenW(value) * multiplier;
6734 size += mod_size * sizeof(WCHAR);
6737 newval = msi_alloc(size);
6738 ptr = newval;
6739 if (!newval)
6741 res = ERROR_OUTOFMEMORY;
6742 goto done;
6745 if (flags & ENV_MOD_PREFIX)
6747 lstrcpyW(newval, value);
6748 ptr = newval + lstrlenW(value);
6749 action |= 0x80000000;
6752 lstrcpyW(ptr, data);
6754 if (flags & ENV_MOD_APPEND)
6756 lstrcatW(newval, value);
6757 action |= 0x40000000;
6760 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6761 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6762 if (res)
6764 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6767 done:
6768 uirow = MSI_CreateRecord( 3 );
6769 MSI_RecordSetStringW( uirow, 1, name );
6770 MSI_RecordSetStringW( uirow, 2, newval );
6771 MSI_RecordSetInteger( uirow, 3, action );
6772 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6773 msiobj_release( &uirow->hdr );
6775 if (env) RegCloseKey(env);
6776 msi_free(deformatted);
6777 msi_free(data);
6778 msi_free(newval);
6779 return res;
6782 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6784 static const WCHAR query[] = {
6785 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6786 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6787 MSIQUERY *view;
6788 UINT rc;
6790 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6791 if (rc != ERROR_SUCCESS)
6792 return ERROR_SUCCESS;
6794 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6795 msiobj_release(&view->hdr);
6796 return rc;
6799 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6801 MSIPACKAGE *package = param;
6802 LPCWSTR name, value, component;
6803 LPWSTR deformatted = NULL;
6804 DWORD flags;
6805 HKEY env;
6806 MSICOMPONENT *comp;
6807 MSIRECORD *uirow;
6808 int action = 0;
6809 LONG res;
6810 UINT r;
6812 component = MSI_RecordGetString( rec, 4 );
6813 comp = msi_get_loaded_component( package, component );
6814 if (!comp)
6815 return ERROR_SUCCESS;
6817 comp->Action = msi_get_component_action( package, comp );
6818 if (comp->Action != INSTALLSTATE_ABSENT)
6820 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6821 return ERROR_SUCCESS;
6823 name = MSI_RecordGetString( rec, 2 );
6824 value = MSI_RecordGetString( rec, 3 );
6826 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6828 r = env_parse_flags( &name, &value, &flags );
6829 if (r != ERROR_SUCCESS)
6830 return r;
6832 if (!(flags & ENV_ACT_REMOVE))
6834 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6835 return ERROR_SUCCESS;
6838 if (value && !deformat_string( package, value, &deformatted ))
6839 return ERROR_OUTOFMEMORY;
6841 value = deformatted;
6843 r = open_env_key( flags, &env );
6844 if (r != ERROR_SUCCESS)
6846 r = ERROR_SUCCESS;
6847 goto done;
6850 if (flags & ENV_MOD_MACHINE)
6851 action |= 0x20000000;
6853 TRACE("Removing %s\n", debugstr_w(name));
6855 res = RegDeleteValueW( env, name );
6856 if (res != ERROR_SUCCESS)
6858 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6859 r = ERROR_SUCCESS;
6862 done:
6863 uirow = MSI_CreateRecord( 3 );
6864 MSI_RecordSetStringW( uirow, 1, name );
6865 MSI_RecordSetStringW( uirow, 2, value );
6866 MSI_RecordSetInteger( uirow, 3, action );
6867 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6868 msiobj_release( &uirow->hdr );
6870 if (env) RegCloseKey( env );
6871 msi_free( deformatted );
6872 return r;
6875 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6877 static const WCHAR query[] = {
6878 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6879 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6880 MSIQUERY *view;
6881 UINT rc;
6883 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6884 if (rc != ERROR_SUCCESS)
6885 return ERROR_SUCCESS;
6887 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6888 msiobj_release( &view->hdr );
6889 return rc;
6892 UINT msi_validate_product_id( MSIPACKAGE *package )
6894 LPWSTR key, template, id;
6895 UINT r = ERROR_SUCCESS;
6897 id = msi_dup_property( package->db, szProductID );
6898 if (id)
6900 msi_free( id );
6901 return ERROR_SUCCESS;
6903 template = msi_dup_property( package->db, szPIDTemplate );
6904 key = msi_dup_property( package->db, szPIDKEY );
6905 if (key && template)
6907 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6908 r = msi_set_property( package->db, szProductID, key );
6910 msi_free( template );
6911 msi_free( key );
6912 return r;
6915 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6917 return msi_validate_product_id( package );
6920 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6922 TRACE("\n");
6923 package->need_reboot_at_end = 1;
6924 return ERROR_SUCCESS;
6927 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6929 static const WCHAR szAvailableFreeReg[] =
6930 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6931 MSIRECORD *uirow;
6932 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6934 TRACE("%p %d kilobytes\n", package, space);
6936 uirow = MSI_CreateRecord( 1 );
6937 MSI_RecordSetInteger( uirow, 1, space );
6938 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6939 msiobj_release( &uirow->hdr );
6941 return ERROR_SUCCESS;
6944 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6946 TRACE("%p\n", package);
6948 msi_set_property( package->db, szRollbackDisabled, szOne );
6949 return ERROR_SUCCESS;
6952 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6954 FIXME("%p\n", package);
6955 return ERROR_SUCCESS;
6958 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6960 static const WCHAR driver_query[] = {
6961 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6962 'O','D','B','C','D','r','i','v','e','r',0};
6963 static const WCHAR translator_query[] = {
6964 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6965 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6966 MSIQUERY *view;
6967 UINT r, count;
6969 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6970 if (r == ERROR_SUCCESS)
6972 count = 0;
6973 r = MSI_IterateRecords( view, &count, NULL, package );
6974 msiobj_release( &view->hdr );
6975 if (r != ERROR_SUCCESS)
6976 return r;
6977 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6979 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6980 if (r == ERROR_SUCCESS)
6982 count = 0;
6983 r = MSI_IterateRecords( view, &count, NULL, package );
6984 msiobj_release( &view->hdr );
6985 if (r != ERROR_SUCCESS)
6986 return r;
6987 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6989 return ERROR_SUCCESS;
6992 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6994 MSIPACKAGE *package = param;
6995 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6996 WCHAR *value;
6998 if ((value = msi_dup_property( package->db, property )))
7000 FIXME("remove %s\n", debugstr_w(value));
7001 msi_free( value );
7003 return ERROR_SUCCESS;
7006 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7008 static const WCHAR query[] = {
7009 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
7010 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
7011 MSIQUERY *view;
7012 UINT r;
7014 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7015 if (r == ERROR_SUCCESS)
7017 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7018 msiobj_release( &view->hdr );
7019 if (r != ERROR_SUCCESS)
7020 return r;
7022 return ERROR_SUCCESS;
7025 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7027 MSIPACKAGE *package = param;
7028 int attributes = MSI_RecordGetInteger( rec, 5 );
7030 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7032 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7033 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7034 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7035 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7036 HKEY hkey;
7037 UINT r;
7039 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7041 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7042 if (r != ERROR_SUCCESS)
7043 return ERROR_SUCCESS;
7045 else
7047 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7048 if (r != ERROR_SUCCESS)
7049 return ERROR_SUCCESS;
7051 RegCloseKey( hkey );
7053 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7054 debugstr_w(upgrade_code), debugstr_w(version_min),
7055 debugstr_w(version_max), debugstr_w(language));
7057 return ERROR_SUCCESS;
7060 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7062 static const WCHAR query[] = {
7063 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7064 'U','p','g','r','a','d','e',0};
7065 MSIQUERY *view;
7066 UINT r;
7068 if (msi_get_property_int( package->db, szInstalled, 0 ))
7070 TRACE("product is installed, skipping action\n");
7071 return ERROR_SUCCESS;
7073 if (msi_get_property_int( package->db, szPreselected, 0 ))
7075 TRACE("Preselected property is set, not migrating feature states\n");
7076 return ERROR_SUCCESS;
7078 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7079 if (r == ERROR_SUCCESS)
7081 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7082 msiobj_release( &view->hdr );
7083 if (r != ERROR_SUCCESS)
7084 return r;
7086 return ERROR_SUCCESS;
7089 static void bind_image( const char *filename, const char *path )
7091 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7093 WARN("failed to bind image %u\n", GetLastError());
7097 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7099 UINT i;
7100 MSIFILE *file;
7101 MSIPACKAGE *package = param;
7102 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7103 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7104 char *filenameA, *pathA;
7105 WCHAR *pathW, **path_list;
7107 if (!(file = msi_get_loaded_file( package, key )))
7109 WARN("file %s not found\n", debugstr_w(key));
7110 return ERROR_SUCCESS;
7112 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7113 path_list = msi_split_string( paths, ';' );
7114 if (!path_list) bind_image( filenameA, NULL );
7115 else
7117 for (i = 0; path_list[i] && path_list[i][0]; i++)
7119 deformat_string( package, path_list[i], &pathW );
7120 if ((pathA = strdupWtoA( pathW )))
7122 bind_image( filenameA, pathA );
7123 msi_free( pathA );
7125 msi_free( pathW );
7128 msi_free( path_list );
7129 msi_free( filenameA );
7130 return ERROR_SUCCESS;
7133 static UINT ACTION_BindImage( MSIPACKAGE *package )
7135 static const WCHAR query[] = {
7136 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7137 'B','i','n','d','I','m','a','g','e',0};
7138 MSIQUERY *view;
7139 UINT r;
7141 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7142 if (r == ERROR_SUCCESS)
7144 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7145 msiobj_release( &view->hdr );
7146 if (r != ERROR_SUCCESS)
7147 return r;
7149 return ERROR_SUCCESS;
7152 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7154 static const WCHAR query[] = {
7155 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7156 MSIQUERY *view;
7157 DWORD count = 0;
7158 UINT r;
7160 r = MSI_OpenQuery( package->db, &view, query, table );
7161 if (r == ERROR_SUCCESS)
7163 r = MSI_IterateRecords(view, &count, NULL, package);
7164 msiobj_release(&view->hdr);
7165 if (r != ERROR_SUCCESS)
7166 return r;
7168 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7169 return ERROR_SUCCESS;
7172 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7174 static const WCHAR table[] = {
7175 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7176 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7179 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7181 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7182 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7185 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7187 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7188 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7191 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7193 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7194 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7197 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7199 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7200 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7203 static const struct
7205 const WCHAR *action;
7206 UINT (*handler)(MSIPACKAGE *);
7207 const WCHAR *action_rollback;
7209 StandardActions[] =
7211 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7212 { szAppSearch, ACTION_AppSearch, NULL },
7213 { szBindImage, ACTION_BindImage, NULL },
7214 { szCCPSearch, ACTION_CCPSearch, NULL },
7215 { szCostFinalize, ACTION_CostFinalize, NULL },
7216 { szCostInitialize, ACTION_CostInitialize, NULL },
7217 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7218 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7219 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7220 { szDisableRollback, ACTION_DisableRollback, NULL },
7221 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7222 { szExecuteAction, ACTION_ExecuteAction, NULL },
7223 { szFileCost, ACTION_FileCost, NULL },
7224 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7225 { szForceReboot, ACTION_ForceReboot, NULL },
7226 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7227 { szInstallExecute, ACTION_InstallExecute, NULL },
7228 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7229 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7230 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7231 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7232 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7233 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7234 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7235 { szInstallValidate, ACTION_InstallValidate, NULL },
7236 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7237 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7238 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7239 { szMoveFiles, ACTION_MoveFiles, NULL },
7240 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7241 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7242 { szPatchFiles, ACTION_PatchFiles, NULL },
7243 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7244 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7245 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7246 { szPublishProduct, ACTION_PublishProduct, NULL },
7247 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7248 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7249 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7250 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7251 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7252 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7253 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7254 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7255 { szRegisterUser, ACTION_RegisterUser, NULL },
7256 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7257 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7258 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7259 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7260 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7261 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7262 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7263 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7264 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7265 { szResolveSource, ACTION_ResolveSource, NULL },
7266 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7267 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7268 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7269 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7270 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7271 { szStartServices, ACTION_StartServices, szStopServices },
7272 { szStopServices, ACTION_StopServices, szStartServices },
7273 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7274 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7275 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7276 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7277 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7278 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7279 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7280 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7281 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7282 { szValidateProductID, ACTION_ValidateProductID, NULL },
7283 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7284 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7285 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7286 { NULL, NULL, NULL }
7289 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7291 BOOL ret = FALSE;
7292 UINT i;
7294 i = 0;
7295 while (StandardActions[i].action != NULL)
7297 if (!strcmpW( StandardActions[i].action, action ))
7299 ui_actionstart( package, action );
7300 if (StandardActions[i].handler)
7302 ui_actioninfo( package, action, TRUE, 0 );
7303 *rc = StandardActions[i].handler( package );
7304 ui_actioninfo( package, action, FALSE, *rc );
7306 if (StandardActions[i].action_rollback && !package->need_rollback)
7308 TRACE("scheduling rollback action\n");
7309 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7312 else
7314 FIXME("unhandled standard action %s\n", debugstr_w(action));
7315 *rc = ERROR_SUCCESS;
7317 ret = TRUE;
7318 break;
7320 i++;
7322 return ret;
7325 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7327 UINT rc = ERROR_SUCCESS;
7328 BOOL handled;
7330 TRACE("Performing action (%s)\n", debugstr_w(action));
7332 handled = ACTION_HandleStandardAction(package, action, &rc);
7334 if (!handled)
7335 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7337 if (!handled)
7339 WARN("unhandled msi action %s\n", debugstr_w(action));
7340 rc = ERROR_FUNCTION_NOT_CALLED;
7343 return rc;
7346 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7348 UINT rc = ERROR_SUCCESS;
7349 BOOL handled = FALSE;
7351 TRACE("Performing action (%s)\n", debugstr_w(action));
7353 package->action_progress_increment = 0;
7354 handled = ACTION_HandleStandardAction(package, action, &rc);
7356 if (!handled)
7357 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7359 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7360 handled = TRUE;
7362 if (!handled)
7364 WARN("unhandled msi action %s\n", debugstr_w(action));
7365 rc = ERROR_FUNCTION_NOT_CALLED;
7368 return rc;
7371 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7373 UINT rc = ERROR_SUCCESS;
7374 MSIRECORD *row;
7376 static const WCHAR query[] =
7377 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7378 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7379 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7380 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7381 static const WCHAR ui_query[] =
7382 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7383 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7384 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7385 ' ', '=',' ','%','i',0};
7387 if (needs_ui_sequence(package))
7388 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7389 else
7390 row = MSI_QueryGetRecord(package->db, query, seq);
7392 if (row)
7394 LPCWSTR action, cond;
7396 TRACE("Running the actions\n");
7398 /* check conditions */
7399 cond = MSI_RecordGetString(row, 2);
7401 /* this is a hack to skip errors in the condition code */
7402 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7404 msiobj_release(&row->hdr);
7405 return ERROR_SUCCESS;
7408 action = MSI_RecordGetString(row, 1);
7409 if (!action)
7411 ERR("failed to fetch action\n");
7412 msiobj_release(&row->hdr);
7413 return ERROR_FUNCTION_FAILED;
7416 if (needs_ui_sequence(package))
7417 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7418 else
7419 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7421 msiobj_release(&row->hdr);
7424 return rc;
7427 /****************************************************
7428 * TOP level entry points
7429 *****************************************************/
7431 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7432 LPCWSTR szCommandLine )
7434 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7435 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7436 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7437 WCHAR *reinstall = NULL;
7438 BOOL ui_exists;
7439 UINT rc;
7441 msi_set_property( package->db, szAction, szInstall );
7443 package->script->InWhatSequence = SEQUENCE_INSTALL;
7445 if (szPackagePath)
7447 LPWSTR p, dir;
7448 LPCWSTR file;
7450 dir = strdupW(szPackagePath);
7451 p = strrchrW(dir, '\\');
7452 if (p)
7454 *(++p) = 0;
7455 file = szPackagePath + (p - dir);
7457 else
7459 msi_free(dir);
7460 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7461 GetCurrentDirectoryW(MAX_PATH, dir);
7462 lstrcatW(dir, szBackSlash);
7463 file = szPackagePath;
7466 msi_free( package->PackagePath );
7467 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7468 if (!package->PackagePath)
7470 msi_free(dir);
7471 return ERROR_OUTOFMEMORY;
7474 lstrcpyW(package->PackagePath, dir);
7475 lstrcatW(package->PackagePath, file);
7476 msi_free(dir);
7478 msi_set_sourcedir_props(package, FALSE);
7481 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7482 if (rc != ERROR_SUCCESS)
7483 return rc;
7485 msi_apply_transforms( package );
7486 msi_apply_patches( package );
7488 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7490 TRACE("setting reinstall property\n");
7491 msi_set_property( package->db, szReinstall, szAll );
7494 /* properties may have been added by a transform */
7495 msi_clone_properties( package );
7497 msi_parse_command_line( package, szCommandLine, FALSE );
7498 msi_adjust_privilege_properties( package );
7499 msi_set_context( package );
7501 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7503 TRACE("disabling rollback\n");
7504 msi_set_property( package->db, szRollbackDisabled, szOne );
7507 if (needs_ui_sequence( package))
7509 package->script->InWhatSequence |= SEQUENCE_UI;
7510 rc = ACTION_ProcessUISequence(package);
7511 ui_exists = ui_sequence_exists(package);
7512 if (rc == ERROR_SUCCESS || !ui_exists)
7514 package->script->InWhatSequence |= SEQUENCE_EXEC;
7515 rc = ACTION_ProcessExecSequence(package, ui_exists);
7518 else
7519 rc = ACTION_ProcessExecSequence(package, FALSE);
7521 package->script->CurrentlyScripting = FALSE;
7523 /* process the ending type action */
7524 if (rc == ERROR_SUCCESS)
7525 ACTION_PerformActionSequence(package, -1);
7526 else if (rc == ERROR_INSTALL_USEREXIT)
7527 ACTION_PerformActionSequence(package, -2);
7528 else if (rc == ERROR_INSTALL_SUSPEND)
7529 ACTION_PerformActionSequence(package, -4);
7530 else /* failed */
7532 ACTION_PerformActionSequence(package, -3);
7533 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7535 package->need_rollback = TRUE;
7539 /* finish up running custom actions */
7540 ACTION_FinishCustomActions(package);
7542 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7544 WARN("installation failed, running rollback script\n");
7545 execute_script( package, SCRIPT_ROLLBACK );
7547 msi_free( reinstall );
7549 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7550 return ERROR_SUCCESS_REBOOT_REQUIRED;
7552 return rc;