msi: Avoid dead code in parse_prop (Coverity).
[wine/multimedia.git] / dlls / msi / action.c
blob663a9170ad40e7a934e25a7d077215ffb9ede922
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 INT level = msi_get_property_int(package->db, szUILevel, 0);
484 return (level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
487 UINT msi_set_context(MSIPACKAGE *package)
489 UINT r = msi_locate_product( package->ProductCode, &package->Context );
490 if (r != ERROR_SUCCESS)
492 int num = msi_get_property_int( package->db, szAllUsers, 0 );
493 if (num == 1 || num == 2)
494 package->Context = MSIINSTALLCONTEXT_MACHINE;
495 else
496 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
498 return ERROR_SUCCESS;
501 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
503 UINT rc;
504 LPCWSTR cond, action;
505 MSIPACKAGE *package = param;
507 action = MSI_RecordGetString(row,1);
508 if (!action)
510 ERR("Error is retrieving action name\n");
511 return ERROR_FUNCTION_FAILED;
514 /* check conditions */
515 cond = MSI_RecordGetString(row,2);
517 /* this is a hack to skip errors in the condition code */
518 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
520 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
521 return ERROR_SUCCESS;
524 if (needs_ui_sequence(package))
525 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
526 else
527 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
529 msi_dialog_check_messages( NULL );
531 if (package->CurrentInstallState != ERROR_SUCCESS)
532 rc = package->CurrentInstallState;
534 if (rc == ERROR_FUNCTION_NOT_CALLED)
535 rc = ERROR_SUCCESS;
537 if (rc != ERROR_SUCCESS)
538 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
540 return rc;
543 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
545 static const WCHAR query[] = {
546 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
547 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
548 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
549 '`','S','e','q','u','e','n','c','e','`',0};
550 MSIQUERY *view;
551 UINT r;
553 TRACE("%p %s\n", package, debugstr_w(table));
555 r = MSI_OpenQuery( package->db, &view, query, table );
556 if (r == ERROR_SUCCESS)
558 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
559 msiobj_release(&view->hdr);
561 return r;
564 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
566 static const WCHAR query[] = {
567 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
568 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
569 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
570 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
571 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
572 static const WCHAR query_validate[] = {
573 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
574 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
575 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
576 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
577 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
578 MSIQUERY *view;
579 INT seq = 0;
580 UINT rc;
582 if (package->script->ExecuteSequenceRun)
584 TRACE("Execute Sequence already Run\n");
585 return ERROR_SUCCESS;
588 package->script->ExecuteSequenceRun = TRUE;
590 /* get the sequence number */
591 if (UIran)
593 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
594 if (!row) return ERROR_FUNCTION_FAILED;
595 seq = MSI_RecordGetInteger(row,1);
596 msiobj_release(&row->hdr);
598 rc = MSI_OpenQuery(package->db, &view, query, seq);
599 if (rc == ERROR_SUCCESS)
601 TRACE("Running the actions\n");
603 msi_set_property(package->db, szSourceDir, NULL);
604 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
605 msiobj_release(&view->hdr);
607 return rc;
610 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
612 static const WCHAR query[] = {
613 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
614 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
615 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
616 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
617 MSIQUERY *view;
618 UINT rc;
620 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
621 if (rc == ERROR_SUCCESS)
623 TRACE("Running the actions\n");
624 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
625 msiobj_release(&view->hdr);
627 return rc;
630 /********************************************************
631 * ACTION helper functions and functions that perform the actions
632 *******************************************************/
633 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
634 UINT* rc, UINT script, BOOL force )
636 BOOL ret=FALSE;
637 UINT arc;
639 arc = ACTION_CustomAction(package, action, script, force);
641 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
643 *rc = arc;
644 ret = TRUE;
646 return ret;
649 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
651 MSICOMPONENT *comp;
653 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
655 if (!strcmpW( Component, comp->Component )) return comp;
657 return NULL;
660 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
662 MSIFEATURE *feature;
664 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
666 if (!strcmpW( Feature, feature->Feature )) return feature;
668 return NULL;
671 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
673 MSIFILE *file;
675 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
677 if (!strcmpW( key, file->File )) return file;
679 return NULL;
682 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
684 MSIFILEPATCH *patch;
686 /* FIXME: There might be more than one patch */
687 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
689 if (!strcmpW( key, patch->File->File )) return patch;
691 return NULL;
694 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
696 MSIFOLDER *folder;
698 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
700 if (!strcmpW( dir, folder->Directory )) return folder;
702 return NULL;
706 * Recursively create all directories in the path.
707 * shamelessly stolen from setupapi/queue.c
709 BOOL msi_create_full_path( const WCHAR *path )
711 BOOL ret = TRUE;
712 WCHAR *new_path;
713 int len;
715 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
716 strcpyW( new_path, path );
718 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
719 new_path[len - 1] = 0;
721 while (!CreateDirectoryW( new_path, NULL ))
723 WCHAR *slash;
724 DWORD last_error = GetLastError();
725 if (last_error == ERROR_ALREADY_EXISTS) break;
726 if (last_error != ERROR_PATH_NOT_FOUND)
728 ret = FALSE;
729 break;
731 if (!(slash = strrchrW( new_path, '\\' )))
733 ret = FALSE;
734 break;
736 len = slash - new_path;
737 new_path[len] = 0;
738 if (!msi_create_full_path( new_path ))
740 ret = FALSE;
741 break;
743 new_path[len] = '\\';
745 msi_free( new_path );
746 return ret;
749 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
751 MSIRECORD *row;
753 row = MSI_CreateRecord( 4 );
754 MSI_RecordSetInteger( row, 1, a );
755 MSI_RecordSetInteger( row, 2, b );
756 MSI_RecordSetInteger( row, 3, c );
757 MSI_RecordSetInteger( row, 4, d );
758 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
759 msiobj_release( &row->hdr );
761 msi_dialog_check_messages( NULL );
764 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
766 static const WCHAR query[] =
767 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
768 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
769 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
770 WCHAR message[1024];
771 MSIRECORD *row = 0;
772 DWORD size;
774 if (!package->LastAction || strcmpW( package->LastAction, action ))
776 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
778 if (MSI_RecordIsNull( row, 3 ))
780 msiobj_release( &row->hdr );
781 return;
783 /* update the cached action format */
784 msi_free( package->ActionFormat );
785 package->ActionFormat = msi_dup_record_field( row, 3 );
786 msi_free( package->LastAction );
787 package->LastAction = strdupW( action );
788 msiobj_release( &row->hdr );
790 size = 1024;
791 MSI_RecordSetStringW( record, 0, package->ActionFormat );
792 MSI_FormatRecordW( package, record, message, &size );
793 row = MSI_CreateRecord( 1 );
794 MSI_RecordSetStringW( row, 1, message );
795 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
796 msiobj_release( &row->hdr );
799 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
801 if (!comp->Enabled)
803 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
804 return INSTALLSTATE_UNKNOWN;
806 if (package->need_rollback) return comp->Installed;
807 return comp->ActionRequest;
810 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
812 if (package->need_rollback) return feature->Installed;
813 return feature->ActionRequest;
816 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
818 MSIPACKAGE *package = param;
819 LPCWSTR dir, component, full_path;
820 MSIRECORD *uirow;
821 MSIFOLDER *folder;
822 MSICOMPONENT *comp;
824 component = MSI_RecordGetString(row, 2);
825 if (!component)
826 return ERROR_SUCCESS;
828 comp = msi_get_loaded_component(package, component);
829 if (!comp)
830 return ERROR_SUCCESS;
832 comp->Action = msi_get_component_action( package, comp );
833 if (comp->Action != INSTALLSTATE_LOCAL)
835 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
836 return ERROR_SUCCESS;
839 dir = MSI_RecordGetString(row,1);
840 if (!dir)
842 ERR("Unable to get folder id\n");
843 return ERROR_SUCCESS;
846 uirow = MSI_CreateRecord(1);
847 MSI_RecordSetStringW(uirow, 1, dir);
848 msi_ui_actiondata(package, szCreateFolders, uirow);
849 msiobj_release(&uirow->hdr);
851 full_path = msi_get_target_folder( package, dir );
852 if (!full_path)
854 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
855 return ERROR_SUCCESS;
857 TRACE("folder is %s\n", debugstr_w(full_path));
859 folder = msi_get_loaded_folder( package, dir );
860 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
861 folder->State = FOLDER_STATE_CREATED;
862 return ERROR_SUCCESS;
865 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
867 static const WCHAR query[] = {
868 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
869 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
870 MSIQUERY *view;
871 UINT rc;
873 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
874 if (rc != ERROR_SUCCESS)
875 return ERROR_SUCCESS;
877 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
878 msiobj_release(&view->hdr);
879 return rc;
882 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
884 MSIPACKAGE *package = param;
885 LPCWSTR dir, component, full_path;
886 MSIRECORD *uirow;
887 MSIFOLDER *folder;
888 MSICOMPONENT *comp;
890 component = MSI_RecordGetString(row, 2);
891 if (!component)
892 return ERROR_SUCCESS;
894 comp = msi_get_loaded_component(package, component);
895 if (!comp)
896 return ERROR_SUCCESS;
898 comp->Action = msi_get_component_action( package, comp );
899 if (comp->Action != INSTALLSTATE_ABSENT)
901 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
902 return ERROR_SUCCESS;
905 dir = MSI_RecordGetString( row, 1 );
906 if (!dir)
908 ERR("Unable to get folder id\n");
909 return ERROR_SUCCESS;
912 full_path = msi_get_target_folder( package, dir );
913 if (!full_path)
915 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
916 return ERROR_SUCCESS;
918 TRACE("folder is %s\n", debugstr_w(full_path));
920 uirow = MSI_CreateRecord( 1 );
921 MSI_RecordSetStringW( uirow, 1, dir );
922 msi_ui_actiondata( package, szRemoveFolders, uirow );
923 msiobj_release( &uirow->hdr );
925 RemoveDirectoryW( full_path );
926 folder = msi_get_loaded_folder( package, dir );
927 folder->State = FOLDER_STATE_REMOVED;
928 return ERROR_SUCCESS;
931 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
933 static const WCHAR query[] = {
934 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
935 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
936 MSIQUERY *view;
937 UINT rc;
939 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
940 if (rc != ERROR_SUCCESS)
941 return ERROR_SUCCESS;
943 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
944 msiobj_release( &view->hdr );
945 return rc;
948 static UINT load_component( MSIRECORD *row, LPVOID param )
950 MSIPACKAGE *package = param;
951 MSICOMPONENT *comp;
953 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
954 if (!comp)
955 return ERROR_FUNCTION_FAILED;
957 list_add_tail( &package->components, &comp->entry );
959 /* fill in the data */
960 comp->Component = msi_dup_record_field( row, 1 );
962 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
964 comp->ComponentId = msi_dup_record_field( row, 2 );
965 comp->Directory = msi_dup_record_field( row, 3 );
966 comp->Attributes = MSI_RecordGetInteger(row,4);
967 comp->Condition = msi_dup_record_field( row, 5 );
968 comp->KeyPath = msi_dup_record_field( row, 6 );
970 comp->Installed = INSTALLSTATE_UNKNOWN;
971 comp->Action = INSTALLSTATE_UNKNOWN;
972 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
974 comp->assembly = msi_load_assembly( package, comp );
975 return ERROR_SUCCESS;
978 UINT msi_load_all_components( MSIPACKAGE *package )
980 static const WCHAR query[] = {
981 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
982 '`','C','o','m','p','o','n','e','n','t','`',0};
983 MSIQUERY *view;
984 UINT r;
986 if (!list_empty(&package->components))
987 return ERROR_SUCCESS;
989 r = MSI_DatabaseOpenViewW( package->db, query, &view );
990 if (r != ERROR_SUCCESS)
991 return r;
993 if (!msi_init_assembly_caches( package ))
995 ERR("can't initialize assembly caches\n");
996 msiobj_release( &view->hdr );
997 return ERROR_FUNCTION_FAILED;
1000 r = MSI_IterateRecords(view, NULL, load_component, package);
1001 msiobj_release(&view->hdr);
1002 msi_destroy_assembly_caches( package );
1003 return r;
1006 typedef struct {
1007 MSIPACKAGE *package;
1008 MSIFEATURE *feature;
1009 } _ilfs;
1011 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1013 ComponentList *cl;
1015 cl = msi_alloc( sizeof (*cl) );
1016 if ( !cl )
1017 return ERROR_NOT_ENOUGH_MEMORY;
1018 cl->component = comp;
1019 list_add_tail( &feature->Components, &cl->entry );
1021 return ERROR_SUCCESS;
1024 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1026 FeatureList *fl;
1028 fl = msi_alloc( sizeof(*fl) );
1029 if ( !fl )
1030 return ERROR_NOT_ENOUGH_MEMORY;
1031 fl->feature = child;
1032 list_add_tail( &parent->Children, &fl->entry );
1034 return ERROR_SUCCESS;
1037 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1039 _ilfs* ilfs = param;
1040 LPCWSTR component;
1041 MSICOMPONENT *comp;
1043 component = MSI_RecordGetString(row,1);
1045 /* check to see if the component is already loaded */
1046 comp = msi_get_loaded_component( ilfs->package, component );
1047 if (!comp)
1049 WARN("ignoring unknown component %s\n", debugstr_w(component));
1050 return ERROR_SUCCESS;
1052 add_feature_component( ilfs->feature, comp );
1053 comp->Enabled = TRUE;
1055 return ERROR_SUCCESS;
1058 static UINT load_feature(MSIRECORD * row, LPVOID param)
1060 static const WCHAR query[] = {
1061 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1062 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1063 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1064 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1065 MSIPACKAGE *package = param;
1066 MSIFEATURE *feature;
1067 MSIQUERY *view;
1068 _ilfs ilfs;
1069 UINT rc;
1071 /* fill in the data */
1073 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1074 if (!feature)
1075 return ERROR_NOT_ENOUGH_MEMORY;
1077 list_init( &feature->Children );
1078 list_init( &feature->Components );
1080 feature->Feature = msi_dup_record_field( row, 1 );
1082 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1084 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1085 feature->Title = msi_dup_record_field( row, 3 );
1086 feature->Description = msi_dup_record_field( row, 4 );
1088 if (!MSI_RecordIsNull(row,5))
1089 feature->Display = MSI_RecordGetInteger(row,5);
1091 feature->Level= MSI_RecordGetInteger(row,6);
1092 feature->Directory = msi_dup_record_field( row, 7 );
1093 feature->Attributes = MSI_RecordGetInteger(row,8);
1095 feature->Installed = INSTALLSTATE_UNKNOWN;
1096 feature->Action = INSTALLSTATE_UNKNOWN;
1097 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1099 list_add_tail( &package->features, &feature->entry );
1101 /* load feature components */
1103 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1104 if (rc != ERROR_SUCCESS)
1105 return ERROR_SUCCESS;
1107 ilfs.package = package;
1108 ilfs.feature = feature;
1110 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1111 msiobj_release(&view->hdr);
1112 return rc;
1115 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1117 MSIPACKAGE *package = param;
1118 MSIFEATURE *parent, *child;
1120 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1121 if (!child)
1122 return ERROR_FUNCTION_FAILED;
1124 if (!child->Feature_Parent)
1125 return ERROR_SUCCESS;
1127 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1128 if (!parent)
1129 return ERROR_FUNCTION_FAILED;
1131 add_feature_child( parent, child );
1132 return ERROR_SUCCESS;
1135 UINT msi_load_all_features( MSIPACKAGE *package )
1137 static const WCHAR query[] = {
1138 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1139 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1140 '`','D','i','s','p','l','a','y','`',0};
1141 MSIQUERY *view;
1142 UINT r;
1144 if (!list_empty(&package->features))
1145 return ERROR_SUCCESS;
1147 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1148 if (r != ERROR_SUCCESS)
1149 return r;
1151 r = MSI_IterateRecords( view, NULL, load_feature, package );
1152 if (r != ERROR_SUCCESS)
1154 msiobj_release( &view->hdr );
1155 return r;
1157 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1158 msiobj_release( &view->hdr );
1159 return r;
1162 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1164 if (!p)
1165 return p;
1166 p = strchrW(p, ch);
1167 if (!p)
1168 return p;
1169 *p = 0;
1170 return p+1;
1173 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1175 static const WCHAR query[] = {
1176 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1177 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1178 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1179 MSIQUERY *view = NULL;
1180 MSIRECORD *row = NULL;
1181 UINT r;
1183 TRACE("%s\n", debugstr_w(file->File));
1185 r = MSI_OpenQuery(package->db, &view, query, file->File);
1186 if (r != ERROR_SUCCESS)
1187 goto done;
1189 r = MSI_ViewExecute(view, NULL);
1190 if (r != ERROR_SUCCESS)
1191 goto done;
1193 r = MSI_ViewFetch(view, &row);
1194 if (r != ERROR_SUCCESS)
1195 goto done;
1197 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1198 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1199 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1200 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1201 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1203 done:
1204 if (view) msiobj_release(&view->hdr);
1205 if (row) msiobj_release(&row->hdr);
1206 return r;
1209 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1211 MSIRECORD *row;
1212 static const WCHAR query[] = {
1213 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1214 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1215 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1217 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1218 if (!row)
1220 WARN("query failed\n");
1221 return ERROR_FUNCTION_FAILED;
1224 file->disk_id = MSI_RecordGetInteger( row, 1 );
1225 msiobj_release( &row->hdr );
1226 return ERROR_SUCCESS;
1229 static UINT load_file(MSIRECORD *row, LPVOID param)
1231 MSIPACKAGE* package = param;
1232 LPCWSTR component;
1233 MSIFILE *file;
1235 /* fill in the data */
1237 file = msi_alloc_zero( sizeof (MSIFILE) );
1238 if (!file)
1239 return ERROR_NOT_ENOUGH_MEMORY;
1241 file->File = msi_dup_record_field( row, 1 );
1243 component = MSI_RecordGetString( row, 2 );
1244 file->Component = msi_get_loaded_component( package, component );
1246 if (!file->Component)
1248 WARN("Component not found: %s\n", debugstr_w(component));
1249 msi_free(file->File);
1250 msi_free(file);
1251 return ERROR_SUCCESS;
1254 file->FileName = msi_dup_record_field( row, 3 );
1255 msi_reduce_to_long_filename( file->FileName );
1257 file->ShortName = msi_dup_record_field( row, 3 );
1258 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1260 file->FileSize = MSI_RecordGetInteger( row, 4 );
1261 file->Version = msi_dup_record_field( row, 5 );
1262 file->Language = msi_dup_record_field( row, 6 );
1263 file->Attributes = MSI_RecordGetInteger( row, 7 );
1264 file->Sequence = MSI_RecordGetInteger( row, 8 );
1266 file->state = msifs_invalid;
1268 /* if the compressed bits are not set in the file attributes,
1269 * then read the information from the package word count property
1271 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1273 file->IsCompressed = FALSE;
1275 else if (file->Attributes &
1276 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1278 file->IsCompressed = TRUE;
1280 else if (file->Attributes & msidbFileAttributesNoncompressed)
1282 file->IsCompressed = FALSE;
1284 else
1286 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1289 load_file_hash(package, file);
1290 load_file_disk_id(package, file);
1292 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1294 list_add_tail( &package->files, &file->entry );
1296 return ERROR_SUCCESS;
1299 static UINT load_all_files(MSIPACKAGE *package)
1301 static const WCHAR query[] = {
1302 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1303 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1304 '`','S','e','q','u','e','n','c','e','`', 0};
1305 MSIQUERY *view;
1306 UINT rc;
1308 if (!list_empty(&package->files))
1309 return ERROR_SUCCESS;
1311 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1312 if (rc != ERROR_SUCCESS)
1313 return ERROR_SUCCESS;
1315 rc = MSI_IterateRecords(view, NULL, load_file, package);
1316 msiobj_release(&view->hdr);
1317 return rc;
1320 static UINT load_media( MSIRECORD *row, LPVOID param )
1322 MSIPACKAGE *package = param;
1323 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1324 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1326 /* FIXME: load external cabinets and directory sources too */
1327 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1328 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1329 return ERROR_SUCCESS;
1332 static UINT load_all_media( MSIPACKAGE *package )
1334 static const WCHAR query[] = {
1335 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1336 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1337 '`','D','i','s','k','I','d','`',0};
1338 MSIQUERY *view;
1339 UINT r;
1341 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1342 if (r != ERROR_SUCCESS)
1343 return ERROR_SUCCESS;
1345 r = MSI_IterateRecords( view, NULL, load_media, package );
1346 msiobj_release( &view->hdr );
1347 return r;
1350 static UINT load_patch(MSIRECORD *row, LPVOID param)
1352 MSIPACKAGE *package = param;
1353 MSIFILEPATCH *patch;
1354 LPWSTR file_key;
1356 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1357 if (!patch)
1358 return ERROR_NOT_ENOUGH_MEMORY;
1360 file_key = msi_dup_record_field( row, 1 );
1361 patch->File = msi_get_loaded_file( package, file_key );
1362 msi_free(file_key);
1364 if( !patch->File )
1366 ERR("Failed to find target for patch in File table\n");
1367 msi_free(patch);
1368 return ERROR_FUNCTION_FAILED;
1371 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1373 /* FIXME: The database should be properly transformed */
1374 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1376 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1377 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1378 patch->IsApplied = FALSE;
1380 /* FIXME:
1381 * Header field - for patch validation.
1382 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1385 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1387 list_add_tail( &package->filepatches, &patch->entry );
1389 return ERROR_SUCCESS;
1392 static UINT load_all_patches(MSIPACKAGE *package)
1394 static const WCHAR query[] = {
1395 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1396 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1397 '`','S','e','q','u','e','n','c','e','`',0};
1398 MSIQUERY *view;
1399 UINT rc;
1401 if (!list_empty(&package->filepatches))
1402 return ERROR_SUCCESS;
1404 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1405 if (rc != ERROR_SUCCESS)
1406 return ERROR_SUCCESS;
1408 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1409 msiobj_release(&view->hdr);
1410 return rc;
1413 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1415 static const WCHAR query[] = {
1416 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1417 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1418 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1419 MSIQUERY *view;
1421 folder->persistent = FALSE;
1422 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1424 if (!MSI_ViewExecute( view, NULL ))
1426 MSIRECORD *rec;
1427 if (!MSI_ViewFetch( view, &rec ))
1429 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1430 folder->persistent = TRUE;
1431 msiobj_release( &rec->hdr );
1434 msiobj_release( &view->hdr );
1436 return ERROR_SUCCESS;
1439 static UINT load_folder( MSIRECORD *row, LPVOID param )
1441 MSIPACKAGE *package = param;
1442 static WCHAR szEmpty[] = { 0 };
1443 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1444 MSIFOLDER *folder;
1446 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1447 list_init( &folder->children );
1448 folder->Directory = msi_dup_record_field( row, 1 );
1449 folder->Parent = msi_dup_record_field( row, 2 );
1450 p = msi_dup_record_field(row, 3);
1452 TRACE("%s\n", debugstr_w(folder->Directory));
1454 /* split src and target dir */
1455 tgt_short = p;
1456 src_short = folder_split_path( p, ':' );
1458 /* split the long and short paths */
1459 tgt_long = folder_split_path( tgt_short, '|' );
1460 src_long = folder_split_path( src_short, '|' );
1462 /* check for no-op dirs */
1463 if (tgt_short && !strcmpW( szDot, tgt_short ))
1464 tgt_short = szEmpty;
1465 if (src_short && !strcmpW( szDot, src_short ))
1466 src_short = szEmpty;
1468 if (!tgt_long)
1469 tgt_long = tgt_short;
1471 if (!src_short) {
1472 src_short = tgt_short;
1473 src_long = tgt_long;
1476 if (!src_long)
1477 src_long = src_short;
1479 /* FIXME: use the target short path too */
1480 folder->TargetDefault = strdupW(tgt_long);
1481 folder->SourceShortPath = strdupW(src_short);
1482 folder->SourceLongPath = strdupW(src_long);
1483 msi_free(p);
1485 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1486 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1487 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1489 load_folder_persistence( package, folder );
1491 list_add_tail( &package->folders, &folder->entry );
1492 return ERROR_SUCCESS;
1495 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1497 FolderList *fl;
1499 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1500 fl->folder = child;
1501 list_add_tail( &parent->children, &fl->entry );
1502 return ERROR_SUCCESS;
1505 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1507 MSIPACKAGE *package = param;
1508 MSIFOLDER *parent, *child;
1510 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1511 return ERROR_FUNCTION_FAILED;
1513 if (!child->Parent) return ERROR_SUCCESS;
1515 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1516 return ERROR_FUNCTION_FAILED;
1518 return add_folder_child( parent, child );
1521 static UINT load_all_folders( MSIPACKAGE *package )
1523 static const WCHAR query[] = {
1524 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1525 '`','D','i','r','e','c','t','o','r','y','`',0};
1526 MSIQUERY *view;
1527 UINT r;
1529 if (!list_empty(&package->folders))
1530 return ERROR_SUCCESS;
1532 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1533 if (r != ERROR_SUCCESS)
1534 return r;
1536 r = MSI_IterateRecords( view, NULL, load_folder, package );
1537 if (r != ERROR_SUCCESS)
1539 msiobj_release( &view->hdr );
1540 return r;
1542 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1543 msiobj_release( &view->hdr );
1544 return r;
1547 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1549 msi_set_property( package->db, szCostingComplete, szZero );
1550 msi_set_property( package->db, szRootDrive, szCRoot );
1552 load_all_folders( package );
1553 msi_load_all_components( package );
1554 msi_load_all_features( package );
1555 load_all_files( package );
1556 load_all_patches( package );
1557 load_all_media( package );
1559 return ERROR_SUCCESS;
1562 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1564 const WCHAR *action = package->script->Actions[script][index];
1565 ui_actionstart( package, action );
1566 TRACE("executing %s\n", debugstr_w(action));
1567 return ACTION_PerformAction( package, action, script );
1570 static UINT execute_script( MSIPACKAGE *package, UINT script )
1572 UINT i, rc = ERROR_SUCCESS;
1574 TRACE("executing script %u\n", script);
1576 if (!package->script)
1578 ERR("no script!\n");
1579 return ERROR_FUNCTION_FAILED;
1581 if (script == SCRIPT_ROLLBACK)
1583 for (i = package->script->ActionCount[script]; i > 0; i--)
1585 rc = execute_script_action( package, script, i - 1 );
1586 if (rc != ERROR_SUCCESS) break;
1589 else
1591 for (i = 0; i < package->script->ActionCount[script]; i++)
1593 rc = execute_script_action( package, script, i );
1594 if (rc != ERROR_SUCCESS) break;
1597 msi_free_action_script(package, script);
1598 return rc;
1601 static UINT ACTION_FileCost(MSIPACKAGE *package)
1603 return ERROR_SUCCESS;
1606 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1608 MSICOMPONENT *comp;
1609 UINT r;
1611 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1613 if (!comp->ComponentId) continue;
1615 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1616 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1617 &comp->Installed );
1618 if (r != ERROR_SUCCESS)
1619 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1620 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1621 &comp->Installed );
1622 if (r != ERROR_SUCCESS)
1623 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1624 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1625 &comp->Installed );
1626 if (r != ERROR_SUCCESS)
1627 comp->Installed = INSTALLSTATE_ABSENT;
1631 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1633 MSIFEATURE *feature;
1635 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1637 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1639 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1640 feature->Installed = INSTALLSTATE_ABSENT;
1641 else
1642 feature->Installed = state;
1646 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1648 return (feature->Level > 0 && feature->Level <= level);
1651 static BOOL process_state_property(MSIPACKAGE* package, int level,
1652 LPCWSTR property, INSTALLSTATE state)
1654 LPWSTR override;
1655 MSIFEATURE *feature;
1657 override = msi_dup_property( package->db, property );
1658 if (!override)
1659 return FALSE;
1661 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1663 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1664 continue;
1666 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1668 if (!strcmpiW( override, szAll ))
1670 if (feature->Installed != state)
1672 feature->Action = state;
1673 feature->ActionRequest = state;
1676 else
1678 LPWSTR ptr = override;
1679 LPWSTR ptr2 = strchrW(override,',');
1681 while (ptr)
1683 int len = ptr2 - ptr;
1685 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1686 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1688 if (feature->Installed != state)
1690 feature->Action = state;
1691 feature->ActionRequest = state;
1693 break;
1695 if (ptr2)
1697 ptr=ptr2+1;
1698 ptr2 = strchrW(ptr,',');
1700 else
1701 break;
1705 msi_free(override);
1706 return TRUE;
1709 static BOOL process_overrides( MSIPACKAGE *package, int level )
1711 static const WCHAR szAddLocal[] =
1712 {'A','D','D','L','O','C','A','L',0};
1713 static const WCHAR szAddSource[] =
1714 {'A','D','D','S','O','U','R','C','E',0};
1715 static const WCHAR szAdvertise[] =
1716 {'A','D','V','E','R','T','I','S','E',0};
1717 BOOL ret = FALSE;
1719 /* all these activation/deactivation things happen in order and things
1720 * later on the list override things earlier on the list.
1722 * 0 INSTALLLEVEL processing
1723 * 1 ADDLOCAL
1724 * 2 REMOVE
1725 * 3 ADDSOURCE
1726 * 4 ADDDEFAULT
1727 * 5 REINSTALL
1728 * 6 ADVERTISE
1729 * 7 COMPADDLOCAL
1730 * 8 COMPADDSOURCE
1731 * 9 FILEADDLOCAL
1732 * 10 FILEADDSOURCE
1733 * 11 FILEADDDEFAULT
1735 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1736 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1737 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1738 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1739 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1741 if (ret)
1742 msi_set_property( package->db, szPreselected, szOne );
1744 return ret;
1747 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1749 int level;
1750 MSICOMPONENT* component;
1751 MSIFEATURE *feature;
1753 TRACE("Checking Install Level\n");
1755 level = msi_get_property_int(package->db, szInstallLevel, 1);
1757 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1759 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1761 if (!is_feature_selected( feature, level )) continue;
1763 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1765 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1767 feature->Action = INSTALLSTATE_SOURCE;
1768 feature->ActionRequest = INSTALLSTATE_SOURCE;
1770 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1772 feature->Action = INSTALLSTATE_ADVERTISED;
1773 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1775 else
1777 feature->Action = INSTALLSTATE_LOCAL;
1778 feature->ActionRequest = INSTALLSTATE_LOCAL;
1782 /* disable child features of unselected parent or follow parent */
1783 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1785 FeatureList *fl;
1787 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1789 if (!is_feature_selected( feature, level ))
1791 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1792 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1794 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1796 fl->feature->Action = feature->Action;
1797 fl->feature->ActionRequest = feature->ActionRequest;
1802 else /* preselected */
1804 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1806 if (!is_feature_selected( feature, level )) continue;
1808 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1810 if (feature->Installed == INSTALLSTATE_ABSENT)
1812 feature->Action = INSTALLSTATE_UNKNOWN;
1813 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1815 else
1817 feature->Action = feature->Installed;
1818 feature->ActionRequest = feature->Installed;
1824 /* now we want to set component state based based on feature state */
1825 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1827 ComponentList *cl;
1829 TRACE("Examining Feature %s (Level %d Installed %d Request %d Action %d)\n",
1830 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1831 feature->ActionRequest, feature->Action);
1833 if (!is_feature_selected( feature, level )) continue;
1835 /* features with components that have compressed files are made local */
1836 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1838 if (cl->component->ForceLocalState &&
1839 feature->ActionRequest == INSTALLSTATE_SOURCE)
1841 feature->Action = INSTALLSTATE_LOCAL;
1842 feature->ActionRequest = INSTALLSTATE_LOCAL;
1843 break;
1847 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1849 component = cl->component;
1851 switch (feature->ActionRequest)
1853 case INSTALLSTATE_ABSENT:
1854 component->anyAbsent = 1;
1855 break;
1856 case INSTALLSTATE_ADVERTISED:
1857 component->hasAdvertiseFeature = 1;
1858 break;
1859 case INSTALLSTATE_SOURCE:
1860 component->hasSourceFeature = 1;
1861 break;
1862 case INSTALLSTATE_LOCAL:
1863 component->hasLocalFeature = 1;
1864 break;
1865 case INSTALLSTATE_DEFAULT:
1866 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1867 component->hasAdvertiseFeature = 1;
1868 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1869 component->hasSourceFeature = 1;
1870 else
1871 component->hasLocalFeature = 1;
1872 break;
1873 default:
1874 break;
1879 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1881 /* check if it's local or source */
1882 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1883 (component->hasLocalFeature || component->hasSourceFeature))
1885 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1886 !component->ForceLocalState)
1888 component->Action = INSTALLSTATE_SOURCE;
1889 component->ActionRequest = INSTALLSTATE_SOURCE;
1891 else
1893 component->Action = INSTALLSTATE_LOCAL;
1894 component->ActionRequest = INSTALLSTATE_LOCAL;
1896 continue;
1899 /* if any feature is local, the component must be local too */
1900 if (component->hasLocalFeature)
1902 component->Action = INSTALLSTATE_LOCAL;
1903 component->ActionRequest = INSTALLSTATE_LOCAL;
1904 continue;
1906 if (component->hasSourceFeature)
1908 component->Action = INSTALLSTATE_SOURCE;
1909 component->ActionRequest = INSTALLSTATE_SOURCE;
1910 continue;
1912 if (component->hasAdvertiseFeature)
1914 component->Action = INSTALLSTATE_ADVERTISED;
1915 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1916 continue;
1918 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1919 if (component->anyAbsent &&
1920 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1922 component->Action = INSTALLSTATE_ABSENT;
1923 component->ActionRequest = INSTALLSTATE_ABSENT;
1927 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1929 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1931 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1932 component->Action = INSTALLSTATE_LOCAL;
1933 component->ActionRequest = INSTALLSTATE_LOCAL;
1936 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1937 component->Installed == INSTALLSTATE_SOURCE &&
1938 component->hasSourceFeature)
1940 component->Action = INSTALLSTATE_UNKNOWN;
1941 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1944 TRACE("Result: Component %s (Installed %d Request %d Action %d)\n",
1945 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1948 return ERROR_SUCCESS;
1951 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1953 MSIPACKAGE *package = param;
1954 LPCWSTR name;
1955 MSIFEATURE *feature;
1957 name = MSI_RecordGetString( row, 1 );
1959 feature = msi_get_loaded_feature( package, name );
1960 if (!feature)
1961 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1962 else
1964 LPCWSTR Condition;
1965 Condition = MSI_RecordGetString(row,3);
1967 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1969 int level = MSI_RecordGetInteger(row,2);
1970 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1971 feature->Level = level;
1974 return ERROR_SUCCESS;
1977 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1979 static const WCHAR name[] = {'\\',0};
1980 VS_FIXEDFILEINFO *ptr, *ret;
1981 LPVOID version;
1982 DWORD versize, handle;
1983 UINT sz;
1985 versize = GetFileVersionInfoSizeW( filename, &handle );
1986 if (!versize)
1987 return NULL;
1989 version = msi_alloc( versize );
1990 if (!version)
1991 return NULL;
1993 GetFileVersionInfoW( filename, 0, versize, version );
1995 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
1997 msi_free( version );
1998 return NULL;
2001 ret = msi_alloc( sz );
2002 memcpy( ret, ptr, sz );
2004 msi_free( version );
2005 return ret;
2008 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2010 DWORD ms, ls;
2012 msi_parse_version_string( version, &ms, &ls );
2014 if (fi->dwFileVersionMS > ms) return 1;
2015 else if (fi->dwFileVersionMS < ms) return -1;
2016 else if (fi->dwFileVersionLS > ls) return 1;
2017 else if (fi->dwFileVersionLS < ls) return -1;
2018 return 0;
2021 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2023 DWORD ms1, ms2;
2025 msi_parse_version_string( ver1, &ms1, NULL );
2026 msi_parse_version_string( ver2, &ms2, NULL );
2028 if (ms1 > ms2) return 1;
2029 else if (ms1 < ms2) return -1;
2030 return 0;
2033 DWORD msi_get_disk_file_size( LPCWSTR filename )
2035 HANDLE file;
2036 DWORD size;
2038 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2039 if (file == INVALID_HANDLE_VALUE)
2040 return INVALID_FILE_SIZE;
2042 size = GetFileSize( file, NULL );
2043 TRACE("size is %u\n", size);
2044 CloseHandle( file );
2045 return size;
2048 BOOL msi_file_hash_matches( MSIFILE *file )
2050 UINT r;
2051 MSIFILEHASHINFO hash;
2053 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2054 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2055 if (r != ERROR_SUCCESS)
2056 return FALSE;
2058 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2061 static WCHAR *get_temp_dir( void )
2063 static UINT id;
2064 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2066 GetTempPathW( MAX_PATH, tmp );
2067 for (;;)
2069 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2070 if (CreateDirectoryW( dir, NULL )) break;
2072 return strdupW( dir );
2076 * msi_build_directory_name()
2078 * This function is to save messing round with directory names
2079 * It handles adding backslashes between path segments,
2080 * and can add \ at the end of the directory name if told to.
2082 * It takes a variable number of arguments.
2083 * It always allocates a new string for the result, so make sure
2084 * to free the return value when finished with it.
2086 * The first arg is the number of path segments that follow.
2087 * The arguments following count are a list of path segments.
2088 * A path segment may be NULL.
2090 * Path segments will be added with a \ separating them.
2091 * A \ will not be added after the last segment, however if the
2092 * last segment is NULL, then the last character will be a \
2094 WCHAR *msi_build_directory_name( DWORD count, ... )
2096 DWORD sz = 1, i;
2097 WCHAR *dir;
2098 va_list va;
2100 va_start( va, count );
2101 for (i = 0; i < count; i++)
2103 const WCHAR *str = va_arg( va, const WCHAR * );
2104 if (str) sz += strlenW( str ) + 1;
2106 va_end( va );
2108 dir = msi_alloc( sz * sizeof(WCHAR) );
2109 dir[0] = 0;
2111 va_start( va, count );
2112 for (i = 0; i < count; i++)
2114 const WCHAR *str = va_arg( va, const WCHAR * );
2115 if (!str) continue;
2116 strcatW( dir, str );
2117 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2119 va_end( va );
2120 return dir;
2123 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2125 MSIASSEMBLY *assembly = file->Component->assembly;
2127 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2129 msi_free( file->TargetPath );
2130 if (assembly && !assembly->application)
2132 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2133 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2134 msi_track_tempfile( package, file->TargetPath );
2136 else
2138 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2139 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2142 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2145 static UINT calculate_file_cost( MSIPACKAGE *package )
2147 VS_FIXEDFILEINFO *file_version;
2148 WCHAR *font_version;
2149 MSIFILE *file;
2151 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2153 MSICOMPONENT *comp = file->Component;
2154 DWORD file_size;
2156 if (!comp->Enabled) continue;
2158 if (file->IsCompressed)
2159 comp->ForceLocalState = TRUE;
2161 set_target_path( package, file );
2163 if ((comp->assembly && !comp->assembly->installed) ||
2164 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2166 comp->Cost += file->FileSize;
2167 continue;
2169 file_size = msi_get_disk_file_size( file->TargetPath );
2171 if (file->Version)
2173 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2175 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2177 comp->Cost += file->FileSize - file_size;
2179 msi_free( file_version );
2180 continue;
2182 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2184 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2186 comp->Cost += file->FileSize - file_size;
2188 msi_free( font_version );
2189 continue;
2192 if (file_size != file->FileSize)
2194 comp->Cost += file->FileSize - file_size;
2197 return ERROR_SUCCESS;
2200 WCHAR *msi_normalize_path( const WCHAR *in )
2202 const WCHAR *p = in;
2203 WCHAR *q, *ret;
2204 int n, len = strlenW( in ) + 2;
2206 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2208 len = 0;
2209 while (1)
2211 /* copy until the end of the string or a space */
2212 while (*p != ' ' && (*q = *p))
2214 p++, len++;
2215 /* reduce many backslashes to one */
2216 if (*p != '\\' || *q != '\\')
2217 q++;
2220 /* quit at the end of the string */
2221 if (!*p)
2222 break;
2224 /* count the number of spaces */
2225 n = 0;
2226 while (p[n] == ' ')
2227 n++;
2229 /* if it's leading or trailing space, skip it */
2230 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2231 p += n;
2232 else /* copy n spaces */
2233 while (n && (*q++ = *p++)) n--;
2235 while (q - ret > 0 && q[-1] == ' ') q--;
2236 if (q - ret > 0 && q[-1] != '\\')
2238 q[0] = '\\';
2239 q[1] = 0;
2241 return ret;
2244 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2246 FolderList *fl;
2247 MSIFOLDER *folder, *parent, *child;
2248 WCHAR *path, *normalized_path;
2250 TRACE("resolving %s\n", debugstr_w(name));
2252 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2254 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2256 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
2258 path = msi_dup_property( package->db, szRootDrive );
2261 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2263 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2265 parent = msi_get_loaded_folder( package, folder->Parent );
2266 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2268 else
2269 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2271 normalized_path = msi_normalize_path( path );
2272 msi_free( path );
2273 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2275 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2276 msi_free( normalized_path );
2277 return;
2279 msi_set_property( package->db, folder->Directory, normalized_path );
2280 msi_free( folder->ResolvedTarget );
2281 folder->ResolvedTarget = normalized_path;
2283 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2285 child = fl->folder;
2286 msi_resolve_target_folder( package, child->Directory, load_prop );
2288 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2291 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2293 static const WCHAR query[] = {
2294 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2295 '`','C','o','n','d','i','t','i','o','n','`',0};
2296 static const WCHAR szOutOfDiskSpace[] = {
2297 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2298 MSICOMPONENT *comp;
2299 MSIQUERY *view;
2300 LPWSTR level;
2301 UINT rc;
2303 TRACE("Building directory properties\n");
2304 msi_resolve_target_folder( package, szTargetDir, TRUE );
2306 TRACE("Evaluating component conditions\n");
2307 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2309 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2311 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2312 comp->Enabled = FALSE;
2314 else
2315 comp->Enabled = TRUE;
2318 /* read components states from the registry */
2319 ACTION_GetComponentInstallStates(package);
2320 ACTION_GetFeatureInstallStates(package);
2322 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2324 TRACE("Evaluating feature conditions\n");
2326 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2327 if (rc == ERROR_SUCCESS)
2329 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2330 msiobj_release( &view->hdr );
2331 if (rc != ERROR_SUCCESS)
2332 return rc;
2336 TRACE("Calculating file cost\n");
2337 calculate_file_cost( package );
2339 msi_set_property( package->db, szCostingComplete, szOne );
2340 /* set default run level if not set */
2341 level = msi_dup_property( package->db, szInstallLevel );
2342 if (!level)
2343 msi_set_property( package->db, szInstallLevel, szOne );
2344 msi_free(level);
2346 /* FIXME: check volume disk space */
2347 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2349 return MSI_SetFeatureStates(package);
2352 /* OK this value is "interpreted" and then formatted based on the
2353 first few characters */
2354 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2355 DWORD *size)
2357 LPSTR data = NULL;
2359 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2361 if (value[1]=='x')
2363 LPWSTR ptr;
2364 CHAR byte[5];
2365 LPWSTR deformated = NULL;
2366 int count;
2368 deformat_string(package, &value[2], &deformated);
2370 /* binary value type */
2371 ptr = deformated;
2372 *type = REG_BINARY;
2373 if (strlenW(ptr)%2)
2374 *size = (strlenW(ptr)/2)+1;
2375 else
2376 *size = strlenW(ptr)/2;
2378 data = msi_alloc(*size);
2380 byte[0] = '0';
2381 byte[1] = 'x';
2382 byte[4] = 0;
2383 count = 0;
2384 /* if uneven pad with a zero in front */
2385 if (strlenW(ptr)%2)
2387 byte[2]= '0';
2388 byte[3]= *ptr;
2389 ptr++;
2390 data[count] = (BYTE)strtol(byte,NULL,0);
2391 count ++;
2392 TRACE("Uneven byte count\n");
2394 while (*ptr)
2396 byte[2]= *ptr;
2397 ptr++;
2398 byte[3]= *ptr;
2399 ptr++;
2400 data[count] = (BYTE)strtol(byte,NULL,0);
2401 count ++;
2403 msi_free(deformated);
2405 TRACE("Data %i bytes(%i)\n",*size,count);
2407 else
2409 LPWSTR deformated;
2410 LPWSTR p;
2411 DWORD d = 0;
2412 deformat_string(package, &value[1], &deformated);
2414 *type=REG_DWORD;
2415 *size = sizeof(DWORD);
2416 data = msi_alloc(*size);
2417 p = deformated;
2418 if (*p == '-')
2419 p++;
2420 while (*p)
2422 if ( (*p < '0') || (*p > '9') )
2423 break;
2424 d *= 10;
2425 d += (*p - '0');
2426 p++;
2428 if (deformated[0] == '-')
2429 d = -d;
2430 *(LPDWORD)data = d;
2431 TRACE("DWORD %i\n",*(LPDWORD)data);
2433 msi_free(deformated);
2436 else
2438 static const WCHAR szMulti[] = {'[','~',']',0};
2439 LPCWSTR ptr;
2440 *type=REG_SZ;
2442 if (value[0]=='#')
2444 if (value[1]=='%')
2446 ptr = &value[2];
2447 *type=REG_EXPAND_SZ;
2449 else
2450 ptr = &value[1];
2452 else
2453 ptr=value;
2455 if (strstrW(value, szMulti))
2456 *type = REG_MULTI_SZ;
2458 /* remove initial delimiter */
2459 if (!strncmpW(value, szMulti, 3))
2460 ptr = value + 3;
2462 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2464 /* add double NULL terminator */
2465 if (*type == REG_MULTI_SZ)
2467 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2468 data = msi_realloc_zero(data, *size);
2471 return data;
2474 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2476 const WCHAR *ret;
2478 switch (root)
2480 case -1:
2481 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2483 *root_key = HKEY_LOCAL_MACHINE;
2484 ret = szHLM;
2486 else
2488 *root_key = HKEY_CURRENT_USER;
2489 ret = szHCU;
2491 break;
2492 case 0:
2493 *root_key = HKEY_CLASSES_ROOT;
2494 ret = szHCR;
2495 break;
2496 case 1:
2497 *root_key = HKEY_CURRENT_USER;
2498 ret = szHCU;
2499 break;
2500 case 2:
2501 *root_key = HKEY_LOCAL_MACHINE;
2502 ret = szHLM;
2503 break;
2504 case 3:
2505 *root_key = HKEY_USERS;
2506 ret = szHU;
2507 break;
2508 default:
2509 ERR("Unknown root %i\n", root);
2510 return NULL;
2513 return ret;
2516 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2518 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2519 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2521 if (is_64bit && package->platform == PLATFORM_INTEL &&
2522 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2524 UINT size;
2525 WCHAR *path_32node;
2527 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2528 if (!(path_32node = msi_alloc( size ))) return NULL;
2530 memcpy( path_32node, path, len * sizeof(WCHAR) );
2531 strcpyW( path_32node + len, szWow6432Node );
2532 strcatW( path_32node, szBackSlash );
2533 strcatW( path_32node, path + len );
2534 return path_32node;
2537 return strdupW( path );
2540 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2542 MSIPACKAGE *package = param;
2543 LPSTR value_data = NULL;
2544 HKEY root_key, hkey;
2545 DWORD type,size;
2546 LPWSTR deformated, uikey, keypath;
2547 LPCWSTR szRoot, component, name, key, value;
2548 MSICOMPONENT *comp;
2549 MSIRECORD * uirow;
2550 INT root;
2551 BOOL check_first = FALSE;
2552 UINT rc;
2554 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2556 component = MSI_RecordGetString(row, 6);
2557 comp = msi_get_loaded_component(package,component);
2558 if (!comp)
2559 return ERROR_SUCCESS;
2561 comp->Action = msi_get_component_action( package, comp );
2562 if (comp->Action != INSTALLSTATE_LOCAL)
2564 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2565 return ERROR_SUCCESS;
2568 name = MSI_RecordGetString(row, 4);
2569 if( MSI_RecordIsNull(row,5) && name )
2571 /* null values can have special meanings */
2572 if (name[0]=='-' && name[1] == 0)
2573 return ERROR_SUCCESS;
2574 else if ((name[0]=='+' && name[1] == 0) ||
2575 (name[0] == '*' && name[1] == 0))
2576 name = NULL;
2577 check_first = TRUE;
2580 root = MSI_RecordGetInteger(row,2);
2581 key = MSI_RecordGetString(row, 3);
2583 szRoot = get_root_key( package, root, &root_key );
2584 if (!szRoot)
2585 return ERROR_SUCCESS;
2587 deformat_string(package, key , &deformated);
2588 size = strlenW(deformated) + strlenW(szRoot) + 1;
2589 uikey = msi_alloc(size*sizeof(WCHAR));
2590 strcpyW(uikey,szRoot);
2591 strcatW(uikey,deformated);
2593 keypath = get_keypath( package, root_key, deformated );
2594 msi_free( deformated );
2595 if (RegCreateKeyW( root_key, keypath, &hkey ))
2597 ERR("Could not create key %s\n", debugstr_w(keypath));
2598 msi_free(uikey);
2599 msi_free(keypath);
2600 return ERROR_SUCCESS;
2603 value = MSI_RecordGetString(row,5);
2604 if (value)
2605 value_data = parse_value(package, value, &type, &size);
2606 else
2608 value_data = (LPSTR)strdupW(szEmpty);
2609 size = sizeof(szEmpty);
2610 type = REG_SZ;
2613 deformat_string(package, name, &deformated);
2615 if (!check_first)
2617 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2618 debugstr_w(uikey));
2619 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2621 else
2623 DWORD sz = 0;
2624 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2625 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2627 TRACE("value %s of %s checked already exists\n",
2628 debugstr_w(deformated), debugstr_w(uikey));
2630 else
2632 TRACE("Checked and setting value %s of %s\n",
2633 debugstr_w(deformated), debugstr_w(uikey));
2634 if (deformated || size)
2635 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2638 RegCloseKey(hkey);
2640 uirow = MSI_CreateRecord(3);
2641 MSI_RecordSetStringW(uirow,2,deformated);
2642 MSI_RecordSetStringW(uirow,1,uikey);
2643 if (type == REG_SZ || type == REG_EXPAND_SZ)
2644 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2645 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2646 msiobj_release( &uirow->hdr );
2648 msi_free(value_data);
2649 msi_free(deformated);
2650 msi_free(uikey);
2651 msi_free(keypath);
2653 return ERROR_SUCCESS;
2656 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2658 static const WCHAR query[] = {
2659 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2660 '`','R','e','g','i','s','t','r','y','`',0};
2661 MSIQUERY *view;
2662 UINT rc;
2664 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2665 if (rc != ERROR_SUCCESS)
2666 return ERROR_SUCCESS;
2668 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2669 msiobj_release(&view->hdr);
2670 return rc;
2673 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2675 LONG res;
2676 HKEY hkey;
2677 DWORD num_subkeys, num_values;
2679 if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2681 if ((res = RegDeleteValueW( hkey, value )))
2683 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2685 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2686 NULL, NULL, NULL, NULL );
2687 RegCloseKey( hkey );
2688 if (!res && !num_subkeys && !num_values)
2690 TRACE("removing empty key %s\n", debugstr_w(keypath));
2691 RegDeleteKeyW( root, keypath );
2693 return;
2695 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2698 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2700 LONG res = RegDeleteTreeW( root, keypath );
2701 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2704 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2706 MSIPACKAGE *package = param;
2707 LPCWSTR component, name, key_str, root_key_str;
2708 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2709 MSICOMPONENT *comp;
2710 MSIRECORD *uirow;
2711 BOOL delete_key = FALSE;
2712 HKEY hkey_root;
2713 UINT size;
2714 INT root;
2716 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2718 component = MSI_RecordGetString( row, 6 );
2719 comp = msi_get_loaded_component( package, component );
2720 if (!comp)
2721 return ERROR_SUCCESS;
2723 comp->Action = msi_get_component_action( package, comp );
2724 if (comp->Action != INSTALLSTATE_ABSENT)
2726 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2727 return ERROR_SUCCESS;
2730 name = MSI_RecordGetString( row, 4 );
2731 if (MSI_RecordIsNull( row, 5 ) && name )
2733 if (name[0] == '+' && !name[1])
2734 return ERROR_SUCCESS;
2735 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2737 delete_key = TRUE;
2738 name = NULL;
2742 root = MSI_RecordGetInteger( row, 2 );
2743 key_str = MSI_RecordGetString( row, 3 );
2745 root_key_str = get_root_key( package, root, &hkey_root );
2746 if (!root_key_str)
2747 return ERROR_SUCCESS;
2749 deformat_string( package, key_str, &deformated_key );
2750 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2751 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2752 strcpyW( ui_key_str, root_key_str );
2753 strcatW( ui_key_str, deformated_key );
2755 deformat_string( package, name, &deformated_name );
2757 keypath = get_keypath( package, hkey_root, deformated_key );
2758 msi_free( deformated_key );
2759 if (delete_key) delete_reg_key( hkey_root, keypath );
2760 else delete_reg_value( hkey_root, keypath, deformated_name );
2761 msi_free( keypath );
2763 uirow = MSI_CreateRecord( 2 );
2764 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2765 MSI_RecordSetStringW( uirow, 2, deformated_name );
2766 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2767 msiobj_release( &uirow->hdr );
2769 msi_free( ui_key_str );
2770 msi_free( deformated_name );
2771 return ERROR_SUCCESS;
2774 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2776 MSIPACKAGE *package = param;
2777 LPCWSTR component, name, key_str, root_key_str;
2778 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2779 MSICOMPONENT *comp;
2780 MSIRECORD *uirow;
2781 BOOL delete_key = FALSE;
2782 HKEY hkey_root;
2783 UINT size;
2784 INT root;
2786 component = MSI_RecordGetString( row, 5 );
2787 comp = msi_get_loaded_component( package, component );
2788 if (!comp)
2789 return ERROR_SUCCESS;
2791 comp->Action = msi_get_component_action( package, comp );
2792 if (comp->Action != INSTALLSTATE_LOCAL)
2794 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2795 return ERROR_SUCCESS;
2798 if ((name = MSI_RecordGetString( row, 4 )))
2800 if (name[0] == '-' && !name[1])
2802 delete_key = TRUE;
2803 name = NULL;
2807 root = MSI_RecordGetInteger( row, 2 );
2808 key_str = MSI_RecordGetString( row, 3 );
2810 root_key_str = get_root_key( package, root, &hkey_root );
2811 if (!root_key_str)
2812 return ERROR_SUCCESS;
2814 deformat_string( package, key_str, &deformated_key );
2815 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2816 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2817 strcpyW( ui_key_str, root_key_str );
2818 strcatW( ui_key_str, deformated_key );
2820 deformat_string( package, name, &deformated_name );
2822 keypath = get_keypath( package, hkey_root, deformated_key );
2823 msi_free( deformated_key );
2824 if (delete_key) delete_reg_key( hkey_root, keypath );
2825 else delete_reg_value( hkey_root, keypath, deformated_name );
2826 msi_free( keypath );
2828 uirow = MSI_CreateRecord( 2 );
2829 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2830 MSI_RecordSetStringW( uirow, 2, deformated_name );
2831 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2832 msiobj_release( &uirow->hdr );
2834 msi_free( ui_key_str );
2835 msi_free( deformated_name );
2836 return ERROR_SUCCESS;
2839 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2841 static const WCHAR registry_query[] = {
2842 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2843 '`','R','e','g','i','s','t','r','y','`',0};
2844 static const WCHAR remove_registry_query[] = {
2845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2846 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2847 MSIQUERY *view;
2848 UINT rc;
2850 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2851 if (rc == ERROR_SUCCESS)
2853 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2854 msiobj_release( &view->hdr );
2855 if (rc != ERROR_SUCCESS)
2856 return rc;
2858 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2859 if (rc == ERROR_SUCCESS)
2861 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2862 msiobj_release( &view->hdr );
2863 if (rc != ERROR_SUCCESS)
2864 return rc;
2866 return ERROR_SUCCESS;
2869 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2871 package->script->CurrentlyScripting = TRUE;
2873 return ERROR_SUCCESS;
2877 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2879 static const WCHAR query[]= {
2880 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2881 '`','R','e','g','i','s','t','r','y','`',0};
2882 MSICOMPONENT *comp;
2883 DWORD total = 0, count = 0;
2884 MSIQUERY *view;
2885 MSIFEATURE *feature;
2886 MSIFILE *file;
2887 UINT rc;
2889 TRACE("InstallValidate\n");
2891 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2892 if (rc == ERROR_SUCCESS)
2894 rc = MSI_IterateRecords( view, &count, NULL, package );
2895 msiobj_release( &view->hdr );
2896 if (rc != ERROR_SUCCESS)
2897 return rc;
2898 total += count * REG_PROGRESS_VALUE;
2900 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2901 total += COMPONENT_PROGRESS_VALUE;
2903 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2904 total += file->FileSize;
2906 msi_ui_progress( package, 0, total, 0, 0 );
2908 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2910 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2911 debugstr_w(feature->Feature), feature->Installed,
2912 feature->ActionRequest, feature->Action);
2914 return ERROR_SUCCESS;
2917 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2919 MSIPACKAGE* package = param;
2920 LPCWSTR cond = NULL;
2921 LPCWSTR message = NULL;
2922 UINT r;
2924 static const WCHAR title[]=
2925 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2927 cond = MSI_RecordGetString(row,1);
2929 r = MSI_EvaluateConditionW(package,cond);
2930 if (r == MSICONDITION_FALSE)
2932 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2934 LPWSTR deformated;
2935 message = MSI_RecordGetString(row,2);
2936 deformat_string(package,message,&deformated);
2937 MessageBoxW(NULL,deformated,title,MB_OK);
2938 msi_free(deformated);
2941 return ERROR_INSTALL_FAILURE;
2944 return ERROR_SUCCESS;
2947 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2949 static const WCHAR query[] = {
2950 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2951 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2952 MSIQUERY *view;
2953 UINT rc;
2955 TRACE("Checking launch conditions\n");
2957 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2958 if (rc != ERROR_SUCCESS)
2959 return ERROR_SUCCESS;
2961 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2962 msiobj_release(&view->hdr);
2963 return rc;
2966 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2969 if (!cmp->KeyPath)
2970 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2972 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2974 static const WCHAR query[] = {
2975 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2976 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2977 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2978 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
2979 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2980 MSIRECORD *row;
2981 UINT root, len;
2982 LPWSTR deformated, buffer, deformated_name;
2983 LPCWSTR key, name;
2985 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
2986 if (!row)
2987 return NULL;
2989 root = MSI_RecordGetInteger(row,2);
2990 key = MSI_RecordGetString(row, 3);
2991 name = MSI_RecordGetString(row, 4);
2992 deformat_string(package, key , &deformated);
2993 deformat_string(package, name, &deformated_name);
2995 len = strlenW(deformated) + 6;
2996 if (deformated_name)
2997 len+=strlenW(deformated_name);
2999 buffer = msi_alloc( len *sizeof(WCHAR));
3001 if (deformated_name)
3002 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3003 else
3004 sprintfW(buffer,fmt,root,deformated);
3006 msi_free(deformated);
3007 msi_free(deformated_name);
3008 msiobj_release(&row->hdr);
3010 return buffer;
3012 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3014 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3015 return NULL;
3017 else
3019 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3021 if (file)
3022 return strdupW( file->TargetPath );
3024 return NULL;
3027 static HKEY openSharedDLLsKey(void)
3029 HKEY hkey=0;
3030 static const WCHAR path[] =
3031 {'S','o','f','t','w','a','r','e','\\',
3032 'M','i','c','r','o','s','o','f','t','\\',
3033 'W','i','n','d','o','w','s','\\',
3034 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3035 'S','h','a','r','e','d','D','L','L','s',0};
3037 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3038 return hkey;
3041 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3043 HKEY hkey;
3044 DWORD count=0;
3045 DWORD type;
3046 DWORD sz = sizeof(count);
3047 DWORD rc;
3049 hkey = openSharedDLLsKey();
3050 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3051 if (rc != ERROR_SUCCESS)
3052 count = 0;
3053 RegCloseKey(hkey);
3054 return count;
3057 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3059 HKEY hkey;
3061 hkey = openSharedDLLsKey();
3062 if (count > 0)
3063 msi_reg_set_val_dword( hkey, path, count );
3064 else
3065 RegDeleteValueW(hkey,path);
3066 RegCloseKey(hkey);
3067 return count;
3070 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3072 MSIFEATURE *feature;
3073 INT count = 0;
3074 BOOL write = FALSE;
3076 /* only refcount DLLs */
3077 if (comp->KeyPath == NULL ||
3078 comp->assembly ||
3079 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3080 comp->Attributes & msidbComponentAttributesODBCDataSource)
3081 write = FALSE;
3082 else
3084 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3085 write = (count > 0);
3087 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3088 write = TRUE;
3091 /* increment counts */
3092 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3094 ComponentList *cl;
3096 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3097 continue;
3099 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3101 if ( cl->component == comp )
3102 count++;
3106 /* decrement counts */
3107 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3109 ComponentList *cl;
3111 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3112 continue;
3114 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3116 if ( cl->component == comp )
3117 count--;
3121 /* ref count all the files in the component */
3122 if (write)
3124 MSIFILE *file;
3126 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3128 if (file->Component == comp)
3129 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3133 /* add a count for permanent */
3134 if (comp->Attributes & msidbComponentAttributesPermanent)
3135 count ++;
3137 comp->RefCount = count;
3139 if (write)
3140 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3143 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3145 if (comp->assembly)
3147 const WCHAR prefixW[] = {'<','\\',0};
3148 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3149 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3151 if (keypath)
3153 strcpyW( keypath, prefixW );
3154 strcatW( keypath, comp->assembly->display_name );
3156 return keypath;
3158 return resolve_keypath( package, comp );
3161 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3163 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3164 UINT rc;
3165 MSICOMPONENT *comp;
3166 HKEY hkey;
3168 TRACE("\n");
3170 squash_guid(package->ProductCode,squished_pc);
3171 msi_set_sourcedir_props(package, FALSE);
3173 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3175 MSIRECORD *uirow;
3176 INSTALLSTATE action;
3178 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3179 if (!comp->ComponentId)
3180 continue;
3182 squash_guid( comp->ComponentId, squished_cc );
3183 msi_free( comp->FullKeypath );
3184 comp->FullKeypath = build_full_keypath( package, comp );
3186 ACTION_RefCountComponent( package, comp );
3188 if (package->need_rollback) action = comp->Installed;
3189 else action = comp->ActionRequest;
3191 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3192 debugstr_w(comp->Component), debugstr_w(squished_cc),
3193 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3195 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3197 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3198 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3199 else
3200 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3202 if (rc != ERROR_SUCCESS)
3203 continue;
3205 if (comp->Attributes & msidbComponentAttributesPermanent)
3207 static const WCHAR szPermKey[] =
3208 { '0','0','0','0','0','0','0','0','0','0','0','0',
3209 '0','0','0','0','0','0','0','0','0','0','0','0',
3210 '0','0','0','0','0','0','0','0',0 };
3212 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3214 if (action == INSTALLSTATE_LOCAL)
3215 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3216 else
3218 MSIFILE *file;
3219 MSIRECORD *row;
3220 LPWSTR ptr, ptr2;
3221 WCHAR source[MAX_PATH];
3222 WCHAR base[MAX_PATH];
3223 LPWSTR sourcepath;
3225 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3226 static const WCHAR query[] = {
3227 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3228 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3229 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3230 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3231 '`','D','i','s','k','I','d','`',0};
3233 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3234 continue;
3236 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3237 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3238 ptr2 = strrchrW(source, '\\') + 1;
3239 msiobj_release(&row->hdr);
3241 lstrcpyW(base, package->PackagePath);
3242 ptr = strrchrW(base, '\\');
3243 *(ptr + 1) = '\0';
3245 sourcepath = msi_resolve_file_source(package, file);
3246 ptr = sourcepath + lstrlenW(base);
3247 lstrcpyW(ptr2, ptr);
3248 msi_free(sourcepath);
3250 msi_reg_set_val_str(hkey, squished_pc, source);
3252 RegCloseKey(hkey);
3254 else if (action == INSTALLSTATE_ABSENT)
3256 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3257 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3258 else
3259 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3262 /* UI stuff */
3263 uirow = MSI_CreateRecord(3);
3264 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3265 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3266 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3267 msi_ui_actiondata( package, szProcessComponents, uirow );
3268 msiobj_release( &uirow->hdr );
3270 return ERROR_SUCCESS;
3273 typedef struct {
3274 CLSID clsid;
3275 LPWSTR source;
3277 LPWSTR path;
3278 ITypeLib *ptLib;
3279 } typelib_struct;
3281 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3282 LPWSTR lpszName, LONG_PTR lParam)
3284 TLIBATTR *attr;
3285 typelib_struct *tl_struct = (typelib_struct*) lParam;
3286 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3287 int sz;
3288 HRESULT res;
3290 if (!IS_INTRESOURCE(lpszName))
3292 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3293 return TRUE;
3296 sz = strlenW(tl_struct->source)+4;
3297 sz *= sizeof(WCHAR);
3299 if ((INT_PTR)lpszName == 1)
3300 tl_struct->path = strdupW(tl_struct->source);
3301 else
3303 tl_struct->path = msi_alloc(sz);
3304 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3307 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3308 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3309 if (FAILED(res))
3311 msi_free(tl_struct->path);
3312 tl_struct->path = NULL;
3314 return TRUE;
3317 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3318 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3320 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3321 return FALSE;
3324 msi_free(tl_struct->path);
3325 tl_struct->path = NULL;
3327 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3328 ITypeLib_Release(tl_struct->ptLib);
3330 return TRUE;
3333 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3335 MSIPACKAGE* package = param;
3336 LPCWSTR component;
3337 MSICOMPONENT *comp;
3338 MSIFILE *file;
3339 typelib_struct tl_struct;
3340 ITypeLib *tlib;
3341 HMODULE module;
3342 HRESULT hr;
3344 component = MSI_RecordGetString(row,3);
3345 comp = msi_get_loaded_component(package,component);
3346 if (!comp)
3347 return ERROR_SUCCESS;
3349 comp->Action = msi_get_component_action( package, comp );
3350 if (comp->Action != INSTALLSTATE_LOCAL)
3352 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3353 return ERROR_SUCCESS;
3356 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3358 TRACE("component has no key path\n");
3359 return ERROR_SUCCESS;
3361 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3363 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3364 if (module)
3366 LPCWSTR guid;
3367 guid = MSI_RecordGetString(row,1);
3368 CLSIDFromString( guid, &tl_struct.clsid);
3369 tl_struct.source = strdupW( file->TargetPath );
3370 tl_struct.path = NULL;
3372 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3373 (LONG_PTR)&tl_struct);
3375 if (tl_struct.path)
3377 LPCWSTR helpid, help_path = NULL;
3378 HRESULT res;
3380 helpid = MSI_RecordGetString(row,6);
3382 if (helpid) help_path = msi_get_target_folder( package, helpid );
3383 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3385 if (FAILED(res))
3386 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3387 else
3388 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3390 ITypeLib_Release(tl_struct.ptLib);
3391 msi_free(tl_struct.path);
3393 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3395 FreeLibrary(module);
3396 msi_free(tl_struct.source);
3398 else
3400 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3401 if (FAILED(hr))
3403 ERR("Failed to load type library: %08x\n", hr);
3404 return ERROR_INSTALL_FAILURE;
3407 ITypeLib_Release(tlib);
3410 return ERROR_SUCCESS;
3413 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3415 static const WCHAR query[] = {
3416 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3417 '`','T','y','p','e','L','i','b','`',0};
3418 MSIQUERY *view;
3419 UINT rc;
3421 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3422 if (rc != ERROR_SUCCESS)
3423 return ERROR_SUCCESS;
3425 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3426 msiobj_release(&view->hdr);
3427 return rc;
3430 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3432 MSIPACKAGE *package = param;
3433 LPCWSTR component, guid;
3434 MSICOMPONENT *comp;
3435 GUID libid;
3436 UINT version;
3437 LCID language;
3438 SYSKIND syskind;
3439 HRESULT hr;
3441 component = MSI_RecordGetString( row, 3 );
3442 comp = msi_get_loaded_component( package, component );
3443 if (!comp)
3444 return ERROR_SUCCESS;
3446 comp->Action = msi_get_component_action( package, comp );
3447 if (comp->Action != INSTALLSTATE_ABSENT)
3449 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3450 return ERROR_SUCCESS;
3452 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3454 guid = MSI_RecordGetString( row, 1 );
3455 CLSIDFromString( guid, &libid );
3456 version = MSI_RecordGetInteger( row, 4 );
3457 language = MSI_RecordGetInteger( row, 2 );
3459 #ifdef _WIN64
3460 syskind = SYS_WIN64;
3461 #else
3462 syskind = SYS_WIN32;
3463 #endif
3465 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3466 if (FAILED(hr))
3468 WARN("Failed to unregister typelib: %08x\n", hr);
3471 return ERROR_SUCCESS;
3474 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3476 static const WCHAR query[] = {
3477 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3478 '`','T','y','p','e','L','i','b','`',0};
3479 MSIQUERY *view;
3480 UINT rc;
3482 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3483 if (rc != ERROR_SUCCESS)
3484 return ERROR_SUCCESS;
3486 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3487 msiobj_release( &view->hdr );
3488 return rc;
3491 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3493 static const WCHAR szlnk[] = {'.','l','n','k',0};
3494 LPCWSTR directory, extension, link_folder;
3495 LPWSTR link_file, filename;
3497 directory = MSI_RecordGetString( row, 2 );
3498 link_folder = msi_get_target_folder( package, directory );
3499 if (!link_folder)
3501 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3502 return NULL;
3504 /* may be needed because of a bug somewhere else */
3505 msi_create_full_path( link_folder );
3507 filename = msi_dup_record_field( row, 3 );
3508 msi_reduce_to_long_filename( filename );
3510 extension = strchrW( filename, '.' );
3511 if (!extension || strcmpiW( extension, szlnk ))
3513 int len = strlenW( filename );
3514 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3515 memcpy( filename + len, szlnk, sizeof(szlnk) );
3517 link_file = msi_build_directory_name( 2, link_folder, filename );
3518 msi_free( filename );
3520 return link_file;
3523 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3525 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3526 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3527 WCHAR *folder, *dest, *path;
3529 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3530 folder = msi_dup_property( package->db, szWindowsFolder );
3531 else
3533 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3534 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3535 msi_free( appdata );
3537 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3538 msi_create_full_path( dest );
3539 path = msi_build_directory_name( 2, dest, icon_name );
3540 msi_free( folder );
3541 msi_free( dest );
3542 return path;
3545 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3547 MSIPACKAGE *package = param;
3548 LPWSTR link_file, deformated, path;
3549 LPCWSTR component, target;
3550 MSICOMPONENT *comp;
3551 IShellLinkW *sl = NULL;
3552 IPersistFile *pf = NULL;
3553 HRESULT res;
3555 component = MSI_RecordGetString(row, 4);
3556 comp = msi_get_loaded_component(package, component);
3557 if (!comp)
3558 return ERROR_SUCCESS;
3560 comp->Action = msi_get_component_action( package, comp );
3561 if (comp->Action != INSTALLSTATE_LOCAL)
3563 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3564 return ERROR_SUCCESS;
3566 msi_ui_actiondata( package, szCreateShortcuts, row );
3568 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3569 &IID_IShellLinkW, (LPVOID *) &sl );
3571 if (FAILED( res ))
3573 ERR("CLSID_ShellLink not available\n");
3574 goto err;
3577 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3578 if (FAILED( res ))
3580 ERR("QueryInterface(IID_IPersistFile) failed\n");
3581 goto err;
3584 target = MSI_RecordGetString(row, 5);
3585 if (strchrW(target, '['))
3587 deformat_string( package, target, &path );
3588 TRACE("target path is %s\n", debugstr_w(path));
3589 IShellLinkW_SetPath( sl, path );
3590 msi_free( path );
3592 else
3594 FIXME("poorly handled shortcut format, advertised shortcut\n");
3595 IShellLinkW_SetPath(sl,comp->FullKeypath);
3598 if (!MSI_RecordIsNull(row,6))
3600 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3601 deformat_string(package, arguments, &deformated);
3602 IShellLinkW_SetArguments(sl,deformated);
3603 msi_free(deformated);
3606 if (!MSI_RecordIsNull(row,7))
3608 LPCWSTR description = MSI_RecordGetString(row, 7);
3609 IShellLinkW_SetDescription(sl, description);
3612 if (!MSI_RecordIsNull(row,8))
3613 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3615 if (!MSI_RecordIsNull(row,9))
3617 INT index;
3618 LPCWSTR icon = MSI_RecordGetString(row, 9);
3620 path = msi_build_icon_path(package, icon);
3621 index = MSI_RecordGetInteger(row,10);
3623 /* no value means 0 */
3624 if (index == MSI_NULL_INTEGER)
3625 index = 0;
3627 IShellLinkW_SetIconLocation(sl, path, index);
3628 msi_free(path);
3631 if (!MSI_RecordIsNull(row,11))
3632 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3634 if (!MSI_RecordIsNull(row,12))
3636 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3637 full_path = msi_get_target_folder( package, wkdir );
3638 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3640 link_file = get_link_file(package, row);
3642 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3643 IPersistFile_Save(pf, link_file, FALSE);
3644 msi_free(link_file);
3646 err:
3647 if (pf)
3648 IPersistFile_Release( pf );
3649 if (sl)
3650 IShellLinkW_Release( sl );
3652 return ERROR_SUCCESS;
3655 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3657 static const WCHAR query[] = {
3658 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3659 '`','S','h','o','r','t','c','u','t','`',0};
3660 MSIQUERY *view;
3661 HRESULT res;
3662 UINT rc;
3664 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3665 if (rc != ERROR_SUCCESS)
3666 return ERROR_SUCCESS;
3668 res = CoInitialize( NULL );
3670 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3671 msiobj_release(&view->hdr);
3673 if (SUCCEEDED(res)) CoUninitialize();
3674 return rc;
3677 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3679 MSIPACKAGE *package = param;
3680 LPWSTR link_file;
3681 LPCWSTR component;
3682 MSICOMPONENT *comp;
3684 component = MSI_RecordGetString( row, 4 );
3685 comp = msi_get_loaded_component( package, component );
3686 if (!comp)
3687 return ERROR_SUCCESS;
3689 comp->Action = msi_get_component_action( package, comp );
3690 if (comp->Action != INSTALLSTATE_ABSENT)
3692 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3693 return ERROR_SUCCESS;
3695 msi_ui_actiondata( package, szRemoveShortcuts, row );
3697 link_file = get_link_file( package, row );
3699 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3700 if (!DeleteFileW( link_file ))
3702 WARN("Failed to remove shortcut file %u\n", GetLastError());
3704 msi_free( link_file );
3706 return ERROR_SUCCESS;
3709 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3711 static const WCHAR query[] = {
3712 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3713 '`','S','h','o','r','t','c','u','t','`',0};
3714 MSIQUERY *view;
3715 UINT rc;
3717 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3718 if (rc != ERROR_SUCCESS)
3719 return ERROR_SUCCESS;
3721 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3722 msiobj_release( &view->hdr );
3723 return rc;
3726 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3728 MSIPACKAGE* package = param;
3729 HANDLE the_file;
3730 LPWSTR FilePath;
3731 LPCWSTR FileName;
3732 CHAR buffer[1024];
3733 DWORD sz;
3734 UINT rc;
3736 FileName = MSI_RecordGetString(row,1);
3737 if (!FileName)
3739 ERR("Unable to get FileName\n");
3740 return ERROR_SUCCESS;
3743 FilePath = msi_build_icon_path(package, FileName);
3745 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3747 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3748 FILE_ATTRIBUTE_NORMAL, NULL);
3750 if (the_file == INVALID_HANDLE_VALUE)
3752 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3753 msi_free(FilePath);
3754 return ERROR_SUCCESS;
3759 DWORD write;
3760 sz = 1024;
3761 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3762 if (rc != ERROR_SUCCESS)
3764 ERR("Failed to get stream\n");
3765 CloseHandle(the_file);
3766 DeleteFileW(FilePath);
3767 break;
3769 WriteFile(the_file,buffer,sz,&write,NULL);
3770 } while (sz == 1024);
3772 msi_free(FilePath);
3773 CloseHandle(the_file);
3775 return ERROR_SUCCESS;
3778 static UINT msi_publish_icons(MSIPACKAGE *package)
3780 static const WCHAR query[]= {
3781 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3782 '`','I','c','o','n','`',0};
3783 MSIQUERY *view;
3784 UINT r;
3786 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3787 if (r == ERROR_SUCCESS)
3789 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3790 msiobj_release(&view->hdr);
3791 if (r != ERROR_SUCCESS)
3792 return r;
3794 return ERROR_SUCCESS;
3797 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3799 UINT r;
3800 HKEY source;
3801 LPWSTR buffer;
3802 MSIMEDIADISK *disk;
3803 MSISOURCELISTINFO *info;
3805 r = RegCreateKeyW(hkey, szSourceList, &source);
3806 if (r != ERROR_SUCCESS)
3807 return r;
3809 RegCloseKey(source);
3811 buffer = strrchrW(package->PackagePath, '\\') + 1;
3812 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3813 package->Context, MSICODE_PRODUCT,
3814 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3815 if (r != ERROR_SUCCESS)
3816 return r;
3818 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3819 package->Context, MSICODE_PRODUCT,
3820 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3821 if (r != ERROR_SUCCESS)
3822 return r;
3824 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3825 package->Context, MSICODE_PRODUCT,
3826 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3827 if (r != ERROR_SUCCESS)
3828 return r;
3830 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3832 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3833 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3834 info->options, info->value);
3835 else
3836 MsiSourceListSetInfoW(package->ProductCode, NULL,
3837 info->context, info->options,
3838 info->property, info->value);
3841 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3843 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3844 disk->context, disk->options,
3845 disk->disk_id, disk->volume_label, disk->disk_prompt);
3848 return ERROR_SUCCESS;
3851 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3853 MSIHANDLE hdb, suminfo;
3854 WCHAR guids[MAX_PATH];
3855 WCHAR packcode[SQUISH_GUID_SIZE];
3856 LPWSTR buffer;
3857 LPWSTR ptr;
3858 DWORD langid;
3859 DWORD size;
3860 UINT r;
3862 static const WCHAR szARPProductIcon[] =
3863 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3864 static const WCHAR szAssignment[] =
3865 {'A','s','s','i','g','n','m','e','n','t',0};
3866 static const WCHAR szAdvertiseFlags[] =
3867 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3868 static const WCHAR szClients[] =
3869 {'C','l','i','e','n','t','s',0};
3870 static const WCHAR szColon[] = {':',0};
3872 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3873 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3874 msi_free(buffer);
3876 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3877 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3879 /* FIXME */
3880 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3882 buffer = msi_dup_property(package->db, szARPProductIcon);
3883 if (buffer)
3885 LPWSTR path = msi_build_icon_path(package, buffer);
3886 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3887 msi_free(path);
3888 msi_free(buffer);
3891 buffer = msi_dup_property(package->db, szProductVersion);
3892 if (buffer)
3894 DWORD verdword = msi_version_str_to_dword(buffer);
3895 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3896 msi_free(buffer);
3899 msi_reg_set_val_dword(hkey, szAssignment, 0);
3900 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3901 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3902 msi_reg_set_val_str(hkey, szClients, szColon);
3904 hdb = alloc_msihandle(&package->db->hdr);
3905 if (!hdb)
3906 return ERROR_NOT_ENOUGH_MEMORY;
3908 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3909 MsiCloseHandle(hdb);
3910 if (r != ERROR_SUCCESS)
3911 goto done;
3913 size = MAX_PATH;
3914 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3915 NULL, guids, &size);
3916 if (r != ERROR_SUCCESS)
3917 goto done;
3919 ptr = strchrW(guids, ';');
3920 if (ptr) *ptr = 0;
3921 squash_guid(guids, packcode);
3922 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3924 done:
3925 MsiCloseHandle(suminfo);
3926 return ERROR_SUCCESS;
3929 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3931 UINT r;
3932 HKEY hkey;
3933 LPWSTR upgrade;
3934 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3936 upgrade = msi_dup_property(package->db, szUpgradeCode);
3937 if (!upgrade)
3938 return ERROR_SUCCESS;
3940 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3941 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3942 else
3943 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3945 if (r != ERROR_SUCCESS)
3947 WARN("failed to open upgrade code key\n");
3948 msi_free(upgrade);
3949 return ERROR_SUCCESS;
3951 squash_guid(package->ProductCode, squashed_pc);
3952 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3953 RegCloseKey(hkey);
3954 msi_free(upgrade);
3955 return ERROR_SUCCESS;
3958 static BOOL msi_check_publish(MSIPACKAGE *package)
3960 MSIFEATURE *feature;
3962 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3964 feature->Action = msi_get_feature_action( package, feature );
3965 if (feature->Action == INSTALLSTATE_LOCAL)
3966 return TRUE;
3969 return FALSE;
3972 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3974 MSIFEATURE *feature;
3976 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3978 feature->Action = msi_get_feature_action( package, feature );
3979 if (feature->Action != INSTALLSTATE_ABSENT)
3980 return FALSE;
3983 return TRUE;
3986 static UINT msi_publish_patches( MSIPACKAGE *package )
3988 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
3989 WCHAR patch_squashed[GUID_SIZE];
3990 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
3991 LONG res;
3992 MSIPATCHINFO *patch;
3993 UINT r;
3994 WCHAR *p, *all_patches = NULL;
3995 DWORD len = 0;
3997 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
3998 if (r != ERROR_SUCCESS)
3999 return ERROR_FUNCTION_FAILED;
4001 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4002 if (res != ERROR_SUCCESS)
4004 r = ERROR_FUNCTION_FAILED;
4005 goto done;
4008 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4009 if (r != ERROR_SUCCESS)
4010 goto done;
4012 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4014 squash_guid( patch->patchcode, patch_squashed );
4015 len += strlenW( patch_squashed ) + 1;
4018 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4019 if (!all_patches)
4020 goto done;
4022 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4024 HKEY patch_key;
4026 squash_guid( patch->patchcode, p );
4027 p += strlenW( p ) + 1;
4029 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4030 (const BYTE *)patch->transforms,
4031 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4032 if (res != ERROR_SUCCESS)
4033 goto done;
4035 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4036 if (r != ERROR_SUCCESS)
4037 goto done;
4039 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4040 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4041 RegCloseKey( patch_key );
4042 if (res != ERROR_SUCCESS)
4043 goto done;
4045 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4047 res = GetLastError();
4048 ERR("Unable to copy patch package %d\n", res);
4049 goto done;
4051 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4052 if (res != ERROR_SUCCESS)
4053 goto done;
4055 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4056 RegCloseKey( patch_key );
4057 if (res != ERROR_SUCCESS)
4058 goto done;
4061 all_patches[len] = 0;
4062 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4063 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4064 if (res != ERROR_SUCCESS)
4065 goto done;
4067 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4068 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4069 if (res != ERROR_SUCCESS)
4070 r = ERROR_FUNCTION_FAILED;
4072 done:
4073 RegCloseKey( product_patches_key );
4074 RegCloseKey( patches_key );
4075 RegCloseKey( product_key );
4076 msi_free( all_patches );
4077 return r;
4080 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4082 UINT rc;
4083 HKEY hukey = NULL, hudkey = NULL;
4084 MSIRECORD *uirow;
4086 if (!list_empty(&package->patches))
4088 rc = msi_publish_patches(package);
4089 if (rc != ERROR_SUCCESS)
4090 goto end;
4093 /* FIXME: also need to publish if the product is in advertise mode */
4094 if (!msi_check_publish(package))
4095 return ERROR_SUCCESS;
4097 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4098 &hukey, TRUE);
4099 if (rc != ERROR_SUCCESS)
4100 goto end;
4102 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4103 NULL, &hudkey, TRUE);
4104 if (rc != ERROR_SUCCESS)
4105 goto end;
4107 rc = msi_publish_upgrade_code(package);
4108 if (rc != ERROR_SUCCESS)
4109 goto end;
4111 rc = msi_publish_product_properties(package, hukey);
4112 if (rc != ERROR_SUCCESS)
4113 goto end;
4115 rc = msi_publish_sourcelist(package, hukey);
4116 if (rc != ERROR_SUCCESS)
4117 goto end;
4119 rc = msi_publish_icons(package);
4121 end:
4122 uirow = MSI_CreateRecord( 1 );
4123 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4124 msi_ui_actiondata( package, szPublishProduct, uirow );
4125 msiobj_release( &uirow->hdr );
4127 RegCloseKey(hukey);
4128 RegCloseKey(hudkey);
4129 return rc;
4132 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4134 WCHAR *filename, *ptr, *folder, *ret;
4135 const WCHAR *dirprop;
4137 filename = msi_dup_record_field( row, 2 );
4138 if (filename && (ptr = strchrW( filename, '|' )))
4139 ptr++;
4140 else
4141 ptr = filename;
4143 dirprop = MSI_RecordGetString( row, 3 );
4144 if (dirprop)
4146 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4147 if (!folder) folder = msi_dup_property( package->db, dirprop );
4149 else
4150 folder = msi_dup_property( package->db, szWindowsFolder );
4152 if (!folder)
4154 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4155 msi_free( filename );
4156 return NULL;
4159 ret = msi_build_directory_name( 2, folder, ptr );
4161 msi_free( filename );
4162 msi_free( folder );
4163 return ret;
4166 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4168 MSIPACKAGE *package = param;
4169 LPCWSTR component, section, key, value, identifier;
4170 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4171 MSIRECORD * uirow;
4172 INT action;
4173 MSICOMPONENT *comp;
4175 component = MSI_RecordGetString(row, 8);
4176 comp = msi_get_loaded_component(package,component);
4177 if (!comp)
4178 return ERROR_SUCCESS;
4180 comp->Action = msi_get_component_action( package, comp );
4181 if (comp->Action != INSTALLSTATE_LOCAL)
4183 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4184 return ERROR_SUCCESS;
4187 identifier = MSI_RecordGetString(row,1);
4188 section = MSI_RecordGetString(row,4);
4189 key = MSI_RecordGetString(row,5);
4190 value = MSI_RecordGetString(row,6);
4191 action = MSI_RecordGetInteger(row,7);
4193 deformat_string(package,section,&deformated_section);
4194 deformat_string(package,key,&deformated_key);
4195 deformat_string(package,value,&deformated_value);
4197 fullname = get_ini_file_name(package, row);
4199 if (action == 0)
4201 TRACE("Adding value %s to section %s in %s\n",
4202 debugstr_w(deformated_key), debugstr_w(deformated_section),
4203 debugstr_w(fullname));
4204 WritePrivateProfileStringW(deformated_section, deformated_key,
4205 deformated_value, fullname);
4207 else if (action == 1)
4209 WCHAR returned[10];
4210 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4211 returned, 10, fullname);
4212 if (returned[0] == 0)
4214 TRACE("Adding value %s to section %s in %s\n",
4215 debugstr_w(deformated_key), debugstr_w(deformated_section),
4216 debugstr_w(fullname));
4218 WritePrivateProfileStringW(deformated_section, deformated_key,
4219 deformated_value, fullname);
4222 else if (action == 3)
4223 FIXME("Append to existing section not yet implemented\n");
4225 uirow = MSI_CreateRecord(4);
4226 MSI_RecordSetStringW(uirow,1,identifier);
4227 MSI_RecordSetStringW(uirow,2,deformated_section);
4228 MSI_RecordSetStringW(uirow,3,deformated_key);
4229 MSI_RecordSetStringW(uirow,4,deformated_value);
4230 msi_ui_actiondata( package, szWriteIniValues, uirow );
4231 msiobj_release( &uirow->hdr );
4233 msi_free(fullname);
4234 msi_free(deformated_key);
4235 msi_free(deformated_value);
4236 msi_free(deformated_section);
4237 return ERROR_SUCCESS;
4240 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4242 static const WCHAR query[] = {
4243 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4244 '`','I','n','i','F','i','l','e','`',0};
4245 MSIQUERY *view;
4246 UINT rc;
4248 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4249 if (rc != ERROR_SUCCESS)
4250 return ERROR_SUCCESS;
4252 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4253 msiobj_release(&view->hdr);
4254 return rc;
4257 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4259 MSIPACKAGE *package = param;
4260 LPCWSTR component, section, key, value, identifier;
4261 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4262 MSICOMPONENT *comp;
4263 MSIRECORD *uirow;
4264 INT action;
4266 component = MSI_RecordGetString( row, 8 );
4267 comp = msi_get_loaded_component( package, component );
4268 if (!comp)
4269 return ERROR_SUCCESS;
4271 comp->Action = msi_get_component_action( package, comp );
4272 if (comp->Action != INSTALLSTATE_ABSENT)
4274 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4275 return ERROR_SUCCESS;
4278 identifier = MSI_RecordGetString( row, 1 );
4279 section = MSI_RecordGetString( row, 4 );
4280 key = MSI_RecordGetString( row, 5 );
4281 value = MSI_RecordGetString( row, 6 );
4282 action = MSI_RecordGetInteger( row, 7 );
4284 deformat_string( package, section, &deformated_section );
4285 deformat_string( package, key, &deformated_key );
4286 deformat_string( package, value, &deformated_value );
4288 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4290 filename = get_ini_file_name( package, row );
4292 TRACE("Removing key %s from section %s in %s\n",
4293 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4295 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4297 WARN("Unable to remove key %u\n", GetLastError());
4299 msi_free( filename );
4301 else
4302 FIXME("Unsupported action %d\n", action);
4305 uirow = MSI_CreateRecord( 4 );
4306 MSI_RecordSetStringW( uirow, 1, identifier );
4307 MSI_RecordSetStringW( uirow, 2, deformated_section );
4308 MSI_RecordSetStringW( uirow, 3, deformated_key );
4309 MSI_RecordSetStringW( uirow, 4, deformated_value );
4310 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4311 msiobj_release( &uirow->hdr );
4313 msi_free( deformated_key );
4314 msi_free( deformated_value );
4315 msi_free( deformated_section );
4316 return ERROR_SUCCESS;
4319 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4321 MSIPACKAGE *package = param;
4322 LPCWSTR component, section, key, value, identifier;
4323 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4324 MSICOMPONENT *comp;
4325 MSIRECORD *uirow;
4326 INT action;
4328 component = MSI_RecordGetString( row, 8 );
4329 comp = msi_get_loaded_component( package, component );
4330 if (!comp)
4331 return ERROR_SUCCESS;
4333 comp->Action = msi_get_component_action( package, comp );
4334 if (comp->Action != INSTALLSTATE_LOCAL)
4336 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4337 return ERROR_SUCCESS;
4340 identifier = MSI_RecordGetString( row, 1 );
4341 section = MSI_RecordGetString( row, 4 );
4342 key = MSI_RecordGetString( row, 5 );
4343 value = MSI_RecordGetString( row, 6 );
4344 action = MSI_RecordGetInteger( row, 7 );
4346 deformat_string( package, section, &deformated_section );
4347 deformat_string( package, key, &deformated_key );
4348 deformat_string( package, value, &deformated_value );
4350 if (action == msidbIniFileActionRemoveLine)
4352 filename = get_ini_file_name( package, row );
4354 TRACE("Removing key %s from section %s in %s\n",
4355 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4357 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4359 WARN("Unable to remove key %u\n", GetLastError());
4361 msi_free( filename );
4363 else
4364 FIXME("Unsupported action %d\n", action);
4366 uirow = MSI_CreateRecord( 4 );
4367 MSI_RecordSetStringW( uirow, 1, identifier );
4368 MSI_RecordSetStringW( uirow, 2, deformated_section );
4369 MSI_RecordSetStringW( uirow, 3, deformated_key );
4370 MSI_RecordSetStringW( uirow, 4, deformated_value );
4371 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4372 msiobj_release( &uirow->hdr );
4374 msi_free( deformated_key );
4375 msi_free( deformated_value );
4376 msi_free( deformated_section );
4377 return ERROR_SUCCESS;
4380 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4382 static const WCHAR query[] = {
4383 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4384 '`','I','n','i','F','i','l','e','`',0};
4385 static const WCHAR remove_query[] = {
4386 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4387 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4388 MSIQUERY *view;
4389 UINT rc;
4391 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4392 if (rc == ERROR_SUCCESS)
4394 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4395 msiobj_release( &view->hdr );
4396 if (rc != ERROR_SUCCESS)
4397 return rc;
4399 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4400 if (rc == ERROR_SUCCESS)
4402 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4403 msiobj_release( &view->hdr );
4404 if (rc != ERROR_SUCCESS)
4405 return rc;
4407 return ERROR_SUCCESS;
4410 static void register_dll( const WCHAR *dll, BOOL unregister )
4412 HMODULE hmod;
4414 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4415 if (hmod)
4417 HRESULT (WINAPI *func_ptr)( void );
4418 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4420 func_ptr = (void *)GetProcAddress( hmod, func );
4421 if (func_ptr)
4423 HRESULT hr = func_ptr();
4424 if (FAILED( hr ))
4425 WARN("failed to register dll 0x%08x\n", hr);
4427 else
4428 WARN("entry point %s not found\n", func);
4429 FreeLibrary( hmod );
4430 return;
4432 WARN("failed to load library %u\n", GetLastError());
4435 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4437 MSIPACKAGE *package = param;
4438 LPCWSTR filename;
4439 MSIFILE *file;
4440 MSIRECORD *uirow;
4442 filename = MSI_RecordGetString( row, 1 );
4443 file = msi_get_loaded_file( package, filename );
4444 if (!file)
4446 WARN("unable to find file %s\n", debugstr_w(filename));
4447 return ERROR_SUCCESS;
4449 file->Component->Action = msi_get_component_action( package, file->Component );
4450 if (file->Component->Action != INSTALLSTATE_LOCAL)
4452 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4453 return ERROR_SUCCESS;
4456 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4457 register_dll( file->TargetPath, FALSE );
4459 uirow = MSI_CreateRecord( 2 );
4460 MSI_RecordSetStringW( uirow, 1, file->File );
4461 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4462 msi_ui_actiondata( package, szSelfRegModules, uirow );
4463 msiobj_release( &uirow->hdr );
4465 return ERROR_SUCCESS;
4468 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4470 static const WCHAR query[] = {
4471 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4472 '`','S','e','l','f','R','e','g','`',0};
4473 MSIQUERY *view;
4474 UINT rc;
4476 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4477 if (rc != ERROR_SUCCESS)
4478 return ERROR_SUCCESS;
4480 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4481 msiobj_release(&view->hdr);
4482 return rc;
4485 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4487 MSIPACKAGE *package = param;
4488 LPCWSTR filename;
4489 MSIFILE *file;
4490 MSIRECORD *uirow;
4492 filename = MSI_RecordGetString( row, 1 );
4493 file = msi_get_loaded_file( package, filename );
4494 if (!file)
4496 WARN("unable to find file %s\n", debugstr_w(filename));
4497 return ERROR_SUCCESS;
4499 file->Component->Action = msi_get_component_action( package, file->Component );
4500 if (file->Component->Action != INSTALLSTATE_ABSENT)
4502 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4503 return ERROR_SUCCESS;
4506 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4507 register_dll( file->TargetPath, TRUE );
4509 uirow = MSI_CreateRecord( 2 );
4510 MSI_RecordSetStringW( uirow, 1, file->File );
4511 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4512 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4513 msiobj_release( &uirow->hdr );
4515 return ERROR_SUCCESS;
4518 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4520 static const WCHAR query[] = {
4521 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4522 '`','S','e','l','f','R','e','g','`',0};
4523 MSIQUERY *view;
4524 UINT rc;
4526 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4527 if (rc != ERROR_SUCCESS)
4528 return ERROR_SUCCESS;
4530 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4531 msiobj_release( &view->hdr );
4532 return rc;
4535 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4537 MSIFEATURE *feature;
4538 UINT rc;
4539 HKEY hkey = NULL, userdata = NULL;
4541 if (!msi_check_publish(package))
4542 return ERROR_SUCCESS;
4544 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4545 &hkey, TRUE);
4546 if (rc != ERROR_SUCCESS)
4547 goto end;
4549 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4550 &userdata, TRUE);
4551 if (rc != ERROR_SUCCESS)
4552 goto end;
4554 /* here the guids are base 85 encoded */
4555 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4557 ComponentList *cl;
4558 LPWSTR data = NULL;
4559 GUID clsid;
4560 INT size;
4561 BOOL absent = FALSE;
4562 MSIRECORD *uirow;
4564 if (feature->Action != INSTALLSTATE_LOCAL &&
4565 feature->Action != INSTALLSTATE_SOURCE &&
4566 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4568 size = 1;
4569 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4571 size += 21;
4573 if (feature->Feature_Parent)
4574 size += strlenW( feature->Feature_Parent )+2;
4576 data = msi_alloc(size * sizeof(WCHAR));
4578 data[0] = 0;
4579 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4581 MSICOMPONENT* component = cl->component;
4582 WCHAR buf[21];
4584 buf[0] = 0;
4585 if (component->ComponentId)
4587 TRACE("From %s\n",debugstr_w(component->ComponentId));
4588 CLSIDFromString(component->ComponentId, &clsid);
4589 encode_base85_guid(&clsid,buf);
4590 TRACE("to %s\n",debugstr_w(buf));
4591 strcatW(data,buf);
4595 if (feature->Feature_Parent)
4597 static const WCHAR sep[] = {'\2',0};
4598 strcatW(data,sep);
4599 strcatW(data,feature->Feature_Parent);
4602 msi_reg_set_val_str( userdata, feature->Feature, data );
4603 msi_free(data);
4605 size = 0;
4606 if (feature->Feature_Parent)
4607 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4608 if (!absent)
4610 size += sizeof(WCHAR);
4611 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4612 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4614 else
4616 size += 2*sizeof(WCHAR);
4617 data = msi_alloc(size);
4618 data[0] = 0x6;
4619 data[1] = 0;
4620 if (feature->Feature_Parent)
4621 strcpyW( &data[1], feature->Feature_Parent );
4622 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4623 (LPBYTE)data,size);
4624 msi_free(data);
4627 /* the UI chunk */
4628 uirow = MSI_CreateRecord( 1 );
4629 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4630 msi_ui_actiondata( package, szPublishFeatures, uirow );
4631 msiobj_release( &uirow->hdr );
4632 /* FIXME: call msi_ui_progress? */
4635 end:
4636 RegCloseKey(hkey);
4637 RegCloseKey(userdata);
4638 return rc;
4641 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4643 UINT r;
4644 HKEY hkey;
4645 MSIRECORD *uirow;
4647 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4649 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4650 &hkey, FALSE);
4651 if (r == ERROR_SUCCESS)
4653 RegDeleteValueW(hkey, feature->Feature);
4654 RegCloseKey(hkey);
4657 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4658 &hkey, FALSE);
4659 if (r == ERROR_SUCCESS)
4661 RegDeleteValueW(hkey, feature->Feature);
4662 RegCloseKey(hkey);
4665 uirow = MSI_CreateRecord( 1 );
4666 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4667 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4668 msiobj_release( &uirow->hdr );
4670 return ERROR_SUCCESS;
4673 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4675 MSIFEATURE *feature;
4677 if (!msi_check_unpublish(package))
4678 return ERROR_SUCCESS;
4680 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4682 msi_unpublish_feature(package, feature);
4685 return ERROR_SUCCESS;
4688 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4690 SYSTEMTIME systime;
4691 DWORD size, langid;
4692 WCHAR date[9], *val, *buffer;
4693 const WCHAR *prop, *key;
4695 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4696 static const WCHAR modpath_fmt[] =
4697 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4698 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4699 static const WCHAR szModifyPath[] =
4700 {'M','o','d','i','f','y','P','a','t','h',0};
4701 static const WCHAR szUninstallString[] =
4702 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4703 static const WCHAR szEstimatedSize[] =
4704 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4705 static const WCHAR szDisplayVersion[] =
4706 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4707 static const WCHAR szInstallSource[] =
4708 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4709 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4710 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4711 static const WCHAR szAuthorizedCDFPrefix[] =
4712 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4713 static const WCHAR szARPCONTACT[] =
4714 {'A','R','P','C','O','N','T','A','C','T',0};
4715 static const WCHAR szContact[] =
4716 {'C','o','n','t','a','c','t',0};
4717 static const WCHAR szARPCOMMENTS[] =
4718 {'A','R','P','C','O','M','M','E','N','T','S',0};
4719 static const WCHAR szComments[] =
4720 {'C','o','m','m','e','n','t','s',0};
4721 static const WCHAR szProductName[] =
4722 {'P','r','o','d','u','c','t','N','a','m','e',0};
4723 static const WCHAR szDisplayName[] =
4724 {'D','i','s','p','l','a','y','N','a','m','e',0};
4725 static const WCHAR szARPHELPLINK[] =
4726 {'A','R','P','H','E','L','P','L','I','N','K',0};
4727 static const WCHAR szHelpLink[] =
4728 {'H','e','l','p','L','i','n','k',0};
4729 static const WCHAR szARPHELPTELEPHONE[] =
4730 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4731 static const WCHAR szHelpTelephone[] =
4732 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4733 static const WCHAR szARPINSTALLLOCATION[] =
4734 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4735 static const WCHAR szInstallLocation[] =
4736 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4737 static const WCHAR szManufacturer[] =
4738 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4739 static const WCHAR szPublisher[] =
4740 {'P','u','b','l','i','s','h','e','r',0};
4741 static const WCHAR szARPREADME[] =
4742 {'A','R','P','R','E','A','D','M','E',0};
4743 static const WCHAR szReadme[] =
4744 {'R','e','a','d','M','e',0};
4745 static const WCHAR szARPSIZE[] =
4746 {'A','R','P','S','I','Z','E',0};
4747 static const WCHAR szSize[] =
4748 {'S','i','z','e',0};
4749 static const WCHAR szARPURLINFOABOUT[] =
4750 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4751 static const WCHAR szURLInfoAbout[] =
4752 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4753 static const WCHAR szARPURLUPDATEINFO[] =
4754 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4755 static const WCHAR szURLUpdateInfo[] =
4756 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4757 static const WCHAR szARPSYSTEMCOMPONENT[] =
4758 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4759 static const WCHAR szSystemComponent[] =
4760 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4762 static const WCHAR *propval[] = {
4763 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4764 szARPCONTACT, szContact,
4765 szARPCOMMENTS, szComments,
4766 szProductName, szDisplayName,
4767 szARPHELPLINK, szHelpLink,
4768 szARPHELPTELEPHONE, szHelpTelephone,
4769 szARPINSTALLLOCATION, szInstallLocation,
4770 szSourceDir, szInstallSource,
4771 szManufacturer, szPublisher,
4772 szARPREADME, szReadme,
4773 szARPSIZE, szSize,
4774 szARPURLINFOABOUT, szURLInfoAbout,
4775 szARPURLUPDATEINFO, szURLUpdateInfo,
4776 NULL
4778 const WCHAR **p = propval;
4780 while (*p)
4782 prop = *p++;
4783 key = *p++;
4784 val = msi_dup_property(package->db, prop);
4785 msi_reg_set_val_str(hkey, key, val);
4786 msi_free(val);
4789 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4790 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4792 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4794 size = deformat_string(package, modpath_fmt, &buffer);
4795 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4796 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4797 msi_free(buffer);
4799 /* FIXME: Write real Estimated Size when we have it */
4800 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4802 GetLocalTime(&systime);
4803 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4804 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4806 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4807 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4809 buffer = msi_dup_property(package->db, szProductVersion);
4810 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4811 if (buffer)
4813 DWORD verdword = msi_version_str_to_dword(buffer);
4815 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4816 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4817 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4818 msi_free(buffer);
4821 return ERROR_SUCCESS;
4824 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4826 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4827 MSIRECORD *uirow;
4828 LPWSTR upgrade_code;
4829 HKEY hkey, props, upgrade_key;
4830 UINT rc;
4832 /* FIXME: also need to publish if the product is in advertise mode */
4833 if (!msi_check_publish(package))
4834 return ERROR_SUCCESS;
4836 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4837 if (rc != ERROR_SUCCESS)
4838 return rc;
4840 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4841 if (rc != ERROR_SUCCESS)
4842 goto done;
4844 rc = msi_publish_install_properties(package, hkey);
4845 if (rc != ERROR_SUCCESS)
4846 goto done;
4848 rc = msi_publish_install_properties(package, props);
4849 if (rc != ERROR_SUCCESS)
4850 goto done;
4852 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4853 if (upgrade_code)
4855 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4856 if (rc == ERROR_SUCCESS)
4858 squash_guid( package->ProductCode, squashed_pc );
4859 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4860 RegCloseKey( upgrade_key );
4862 msi_free( upgrade_code );
4864 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4865 package->delete_on_close = FALSE;
4867 done:
4868 uirow = MSI_CreateRecord( 1 );
4869 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4870 msi_ui_actiondata( package, szRegisterProduct, uirow );
4871 msiobj_release( &uirow->hdr );
4873 RegCloseKey(hkey);
4874 return ERROR_SUCCESS;
4877 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4879 return execute_script(package, SCRIPT_INSTALL);
4882 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4884 MSIPACKAGE *package = param;
4885 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4886 WCHAR *p, *icon_path;
4888 if (!icon) return ERROR_SUCCESS;
4889 if ((icon_path = msi_build_icon_path( package, icon )))
4891 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4892 DeleteFileW( icon_path );
4893 if ((p = strrchrW( icon_path, '\\' )))
4895 *p = 0;
4896 RemoveDirectoryW( icon_path );
4898 msi_free( icon_path );
4900 return ERROR_SUCCESS;
4903 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4905 static const WCHAR query[]= {
4906 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4907 MSIQUERY *view;
4908 UINT r;
4910 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4911 if (r == ERROR_SUCCESS)
4913 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4914 msiobj_release( &view->hdr );
4915 if (r != ERROR_SUCCESS)
4916 return r;
4918 return ERROR_SUCCESS;
4921 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4923 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4924 WCHAR *upgrade, **features;
4925 BOOL full_uninstall = TRUE;
4926 MSIFEATURE *feature;
4927 MSIPATCHINFO *patch;
4928 UINT i;
4930 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4932 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4934 features = msi_split_string( remove, ',' );
4935 for (i = 0; features && features[i]; i++)
4937 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4939 msi_free(features);
4941 if (!full_uninstall)
4942 return ERROR_SUCCESS;
4944 MSIREG_DeleteProductKey(package->ProductCode);
4945 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4946 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4948 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4949 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4950 MSIREG_DeleteUserProductKey(package->ProductCode);
4951 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4953 upgrade = msi_dup_property(package->db, szUpgradeCode);
4954 if (upgrade)
4956 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4957 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4958 msi_free(upgrade);
4961 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4963 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4964 if (!strcmpW( package->ProductCode, patch->products ))
4966 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
4967 patch->delete_on_close = TRUE;
4969 /* FIXME: remove local patch package if this is the last product */
4971 TRACE("removing local package %s\n", debugstr_w(package->localfile));
4972 package->delete_on_close = TRUE;
4974 msi_unpublish_icons( package );
4975 return ERROR_SUCCESS;
4978 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4980 UINT rc;
4981 WCHAR *remove;
4983 /* turn off scheduling */
4984 package->script->CurrentlyScripting= FALSE;
4986 /* first do the same as an InstallExecute */
4987 rc = ACTION_InstallExecute(package);
4988 if (rc != ERROR_SUCCESS)
4989 return rc;
4991 /* then handle commit actions */
4992 rc = execute_script(package, SCRIPT_COMMIT);
4993 if (rc != ERROR_SUCCESS)
4994 return rc;
4996 remove = msi_dup_property(package->db, szRemove);
4997 rc = msi_unpublish_product(package, remove);
4998 msi_free(remove);
4999 return rc;
5002 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5004 static const WCHAR RunOnce[] = {
5005 'S','o','f','t','w','a','r','e','\\',
5006 'M','i','c','r','o','s','o','f','t','\\',
5007 'W','i','n','d','o','w','s','\\',
5008 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5009 'R','u','n','O','n','c','e',0};
5010 static const WCHAR InstallRunOnce[] = {
5011 'S','o','f','t','w','a','r','e','\\',
5012 'M','i','c','r','o','s','o','f','t','\\',
5013 'W','i','n','d','o','w','s','\\',
5014 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5015 'I','n','s','t','a','l','l','e','r','\\',
5016 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5018 static const WCHAR msiexec_fmt[] = {
5019 '%','s',
5020 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5021 '\"','%','s','\"',0};
5022 static const WCHAR install_fmt[] = {
5023 '/','I',' ','\"','%','s','\"',' ',
5024 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5025 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5026 WCHAR buffer[256], sysdir[MAX_PATH];
5027 HKEY hkey;
5028 WCHAR squished_pc[100];
5030 squash_guid(package->ProductCode,squished_pc);
5032 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5033 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5034 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5035 squished_pc);
5037 msi_reg_set_val_str( hkey, squished_pc, buffer );
5038 RegCloseKey(hkey);
5040 TRACE("Reboot command %s\n",debugstr_w(buffer));
5042 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5043 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5045 msi_reg_set_val_str( hkey, squished_pc, buffer );
5046 RegCloseKey(hkey);
5048 return ERROR_INSTALL_SUSPEND;
5051 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5053 static const WCHAR query[] =
5054 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5055 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5056 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5057 MSIRECORD *rec, *row;
5058 DWORD i, size = 0;
5059 va_list va;
5060 const WCHAR *str;
5061 WCHAR *data;
5063 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5065 rec = MSI_CreateRecord( count + 2 );
5066 str = MSI_RecordGetString( row, 1 );
5067 MSI_RecordSetStringW( rec, 0, str );
5068 msiobj_release( &row->hdr );
5069 MSI_RecordSetInteger( rec, 1, error );
5071 va_start( va, count );
5072 for (i = 0; i < count; i++)
5074 str = va_arg( va, const WCHAR *);
5075 MSI_RecordSetStringW( rec, i + 2, str );
5077 va_end( va );
5079 MSI_FormatRecordW( package, rec, NULL, &size );
5080 size++;
5081 data = msi_alloc( size * sizeof(WCHAR) );
5082 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5083 else data[0] = 0;
5084 msiobj_release( &rec->hdr );
5085 return data;
5088 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5090 DWORD attrib;
5091 UINT rc;
5094 * We are currently doing what should be done here in the top level Install
5095 * however for Administrative and uninstalls this step will be needed
5097 if (!package->PackagePath)
5098 return ERROR_SUCCESS;
5100 msi_set_sourcedir_props(package, TRUE);
5102 attrib = GetFileAttributesW(package->db->path);
5103 if (attrib == INVALID_FILE_ATTRIBUTES)
5105 LPWSTR prompt, msg;
5106 DWORD size = 0;
5108 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5109 package->Context, MSICODE_PRODUCT,
5110 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5111 if (rc == ERROR_MORE_DATA)
5113 prompt = msi_alloc(size * sizeof(WCHAR));
5114 MsiSourceListGetInfoW(package->ProductCode, NULL,
5115 package->Context, MSICODE_PRODUCT,
5116 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5118 else
5119 prompt = strdupW(package->db->path);
5121 msg = msi_build_error_string(package, 1302, 1, prompt);
5122 msi_free(prompt);
5123 while(attrib == INVALID_FILE_ATTRIBUTES)
5125 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5126 if (rc == IDCANCEL)
5128 msi_free(msg);
5129 return ERROR_INSTALL_USEREXIT;
5131 attrib = GetFileAttributesW(package->db->path);
5133 msi_free(msg);
5134 rc = ERROR_SUCCESS;
5136 else
5137 return ERROR_SUCCESS;
5139 return rc;
5142 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5144 HKEY hkey = 0;
5145 LPWSTR buffer, productid = NULL;
5146 UINT i, rc = ERROR_SUCCESS;
5147 MSIRECORD *uirow;
5149 static const WCHAR szPropKeys[][80] =
5151 {'P','r','o','d','u','c','t','I','D',0},
5152 {'U','S','E','R','N','A','M','E',0},
5153 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5154 {0},
5157 static const WCHAR szRegKeys[][80] =
5159 {'P','r','o','d','u','c','t','I','D',0},
5160 {'R','e','g','O','w','n','e','r',0},
5161 {'R','e','g','C','o','m','p','a','n','y',0},
5162 {0},
5165 if (msi_check_unpublish(package))
5167 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5168 goto end;
5171 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5172 if (!productid)
5173 goto end;
5175 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5176 NULL, &hkey, TRUE);
5177 if (rc != ERROR_SUCCESS)
5178 goto end;
5180 for( i = 0; szPropKeys[i][0]; i++ )
5182 buffer = msi_dup_property( package->db, szPropKeys[i] );
5183 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5184 msi_free( buffer );
5187 end:
5188 uirow = MSI_CreateRecord( 1 );
5189 MSI_RecordSetStringW( uirow, 1, productid );
5190 msi_ui_actiondata( package, szRegisterUser, uirow );
5191 msiobj_release( &uirow->hdr );
5193 msi_free(productid);
5194 RegCloseKey(hkey);
5195 return rc;
5199 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5201 UINT rc;
5203 package->script->InWhatSequence |= SEQUENCE_EXEC;
5204 rc = ACTION_ProcessExecSequence(package,FALSE);
5205 return rc;
5208 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5210 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5211 WCHAR productid_85[21], component_85[21], *ret;
5212 GUID clsid;
5213 DWORD sz;
5215 /* > is used if there is a component GUID and < if not. */
5217 productid_85[0] = 0;
5218 component_85[0] = 0;
5219 CLSIDFromString( package->ProductCode, &clsid );
5221 encode_base85_guid( &clsid, productid_85 );
5222 if (component)
5224 CLSIDFromString( component->ComponentId, &clsid );
5225 encode_base85_guid( &clsid, component_85 );
5228 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5229 debugstr_w(component_85));
5231 sz = 20 + strlenW( feature ) + 20 + 3;
5232 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5233 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5234 return ret;
5237 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5239 MSIPACKAGE *package = param;
5240 LPCWSTR compgroupid, component, feature, qualifier, text;
5241 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5242 HKEY hkey = NULL;
5243 UINT rc;
5244 MSICOMPONENT *comp;
5245 MSIFEATURE *feat;
5246 DWORD sz;
5247 MSIRECORD *uirow;
5248 int len;
5250 feature = MSI_RecordGetString(rec, 5);
5251 feat = msi_get_loaded_feature(package, feature);
5252 if (!feat)
5253 return ERROR_SUCCESS;
5255 feat->Action = msi_get_feature_action( package, feat );
5256 if (feat->Action != INSTALLSTATE_LOCAL &&
5257 feat->Action != INSTALLSTATE_SOURCE &&
5258 feat->Action != INSTALLSTATE_ADVERTISED)
5260 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5261 return ERROR_SUCCESS;
5264 component = MSI_RecordGetString(rec, 3);
5265 comp = msi_get_loaded_component(package, component);
5266 if (!comp)
5267 return ERROR_SUCCESS;
5269 compgroupid = MSI_RecordGetString(rec,1);
5270 qualifier = MSI_RecordGetString(rec,2);
5272 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5273 if (rc != ERROR_SUCCESS)
5274 goto end;
5276 advertise = msi_create_component_advertise_string( package, comp, feature );
5277 text = MSI_RecordGetString( rec, 4 );
5278 if (text)
5280 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5281 strcpyW( p, advertise );
5282 strcatW( p, text );
5283 msi_free( advertise );
5284 advertise = p;
5286 existing = msi_reg_get_val_str( hkey, qualifier );
5288 sz = strlenW( advertise ) + 1;
5289 if (existing)
5291 for (p = existing; *p; p += len)
5293 len = strlenW( p ) + 1;
5294 if (strcmpW( advertise, p )) sz += len;
5297 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5299 rc = ERROR_OUTOFMEMORY;
5300 goto end;
5302 q = output;
5303 if (existing)
5305 for (p = existing; *p; p += len)
5307 len = strlenW( p ) + 1;
5308 if (strcmpW( advertise, p ))
5310 memcpy( q, p, len * sizeof(WCHAR) );
5311 q += len;
5315 strcpyW( q, advertise );
5316 q[strlenW( q ) + 1] = 0;
5318 msi_reg_set_val_multi_str( hkey, qualifier, output );
5320 end:
5321 RegCloseKey(hkey);
5322 msi_free( output );
5323 msi_free( advertise );
5324 msi_free( existing );
5326 /* the UI chunk */
5327 uirow = MSI_CreateRecord( 2 );
5328 MSI_RecordSetStringW( uirow, 1, compgroupid );
5329 MSI_RecordSetStringW( uirow, 2, qualifier);
5330 msi_ui_actiondata( package, szPublishComponents, uirow );
5331 msiobj_release( &uirow->hdr );
5332 /* FIXME: call ui_progress? */
5334 return rc;
5338 * At present I am ignorning the advertised components part of this and only
5339 * focusing on the qualified component sets
5341 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5343 static const WCHAR query[] = {
5344 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5345 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5346 MSIQUERY *view;
5347 UINT rc;
5349 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5350 if (rc != ERROR_SUCCESS)
5351 return ERROR_SUCCESS;
5353 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5354 msiobj_release(&view->hdr);
5355 return rc;
5358 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5360 static const WCHAR szInstallerComponents[] = {
5361 'S','o','f','t','w','a','r','e','\\',
5362 'M','i','c','r','o','s','o','f','t','\\',
5363 'I','n','s','t','a','l','l','e','r','\\',
5364 'C','o','m','p','o','n','e','n','t','s','\\',0};
5366 MSIPACKAGE *package = param;
5367 LPCWSTR compgroupid, component, feature, qualifier;
5368 MSICOMPONENT *comp;
5369 MSIFEATURE *feat;
5370 MSIRECORD *uirow;
5371 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5372 LONG res;
5374 feature = MSI_RecordGetString( rec, 5 );
5375 feat = msi_get_loaded_feature( package, feature );
5376 if (!feat)
5377 return ERROR_SUCCESS;
5379 feat->Action = msi_get_feature_action( package, feat );
5380 if (feat->Action != INSTALLSTATE_ABSENT)
5382 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5383 return ERROR_SUCCESS;
5386 component = MSI_RecordGetString( rec, 3 );
5387 comp = msi_get_loaded_component( package, component );
5388 if (!comp)
5389 return ERROR_SUCCESS;
5391 compgroupid = MSI_RecordGetString( rec, 1 );
5392 qualifier = MSI_RecordGetString( rec, 2 );
5394 squash_guid( compgroupid, squashed );
5395 strcpyW( keypath, szInstallerComponents );
5396 strcatW( keypath, squashed );
5398 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5399 if (res != ERROR_SUCCESS)
5401 WARN("Unable to delete component key %d\n", res);
5404 uirow = MSI_CreateRecord( 2 );
5405 MSI_RecordSetStringW( uirow, 1, compgroupid );
5406 MSI_RecordSetStringW( uirow, 2, qualifier );
5407 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5408 msiobj_release( &uirow->hdr );
5410 return ERROR_SUCCESS;
5413 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5415 static const WCHAR query[] = {
5416 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5417 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5418 MSIQUERY *view;
5419 UINT rc;
5421 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5422 if (rc != ERROR_SUCCESS)
5423 return ERROR_SUCCESS;
5425 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5426 msiobj_release( &view->hdr );
5427 return rc;
5430 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5432 static const WCHAR query[] =
5433 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5434 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5435 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5436 MSIPACKAGE *package = param;
5437 MSICOMPONENT *component;
5438 MSIRECORD *row;
5439 MSIFILE *file;
5440 SC_HANDLE hscm = NULL, service = NULL;
5441 LPCWSTR comp, key;
5442 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5443 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5444 DWORD serv_type, start_type, err_control;
5445 SERVICE_DESCRIPTIONW sd = {NULL};
5447 comp = MSI_RecordGetString( rec, 12 );
5448 component = msi_get_loaded_component( package, comp );
5449 if (!component)
5451 WARN("service component not found\n");
5452 goto done;
5454 component->Action = msi_get_component_action( package, component );
5455 if (component->Action != INSTALLSTATE_LOCAL)
5457 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5458 goto done;
5460 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5461 if (!hscm)
5463 ERR("Failed to open the SC Manager!\n");
5464 goto done;
5467 start_type = MSI_RecordGetInteger(rec, 5);
5468 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5469 goto done;
5471 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5472 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5473 serv_type = MSI_RecordGetInteger(rec, 4);
5474 err_control = MSI_RecordGetInteger(rec, 6);
5475 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5476 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5477 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5478 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5479 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5480 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5482 /* fetch the service path */
5483 row = MSI_QueryGetRecord(package->db, query, comp);
5484 if (!row)
5486 ERR("Query failed\n");
5487 goto done;
5489 key = MSI_RecordGetString(row, 6);
5490 file = msi_get_loaded_file(package, key);
5491 msiobj_release(&row->hdr);
5492 if (!file)
5494 ERR("Failed to load the service file\n");
5495 goto done;
5498 if (!args || !args[0]) image_path = file->TargetPath;
5499 else
5501 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5502 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5503 return ERROR_OUTOFMEMORY;
5505 strcpyW(image_path, file->TargetPath);
5506 strcatW(image_path, szSpace);
5507 strcatW(image_path, args);
5509 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5510 start_type, err_control, image_path, load_order,
5511 NULL, depends, serv_name, pass);
5513 if (!service)
5515 if (GetLastError() != ERROR_SERVICE_EXISTS)
5516 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5518 else if (sd.lpDescription)
5520 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5521 WARN("failed to set service description %u\n", GetLastError());
5524 if (image_path != file->TargetPath) msi_free(image_path);
5525 done:
5526 CloseServiceHandle(service);
5527 CloseServiceHandle(hscm);
5528 msi_free(name);
5529 msi_free(disp);
5530 msi_free(sd.lpDescription);
5531 msi_free(load_order);
5532 msi_free(serv_name);
5533 msi_free(pass);
5534 msi_free(depends);
5535 msi_free(args);
5537 return ERROR_SUCCESS;
5540 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5542 static const WCHAR query[] = {
5543 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5544 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5545 MSIQUERY *view;
5546 UINT rc;
5548 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5549 if (rc != ERROR_SUCCESS)
5550 return ERROR_SUCCESS;
5552 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5553 msiobj_release(&view->hdr);
5554 return rc;
5557 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5558 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5560 LPCWSTR *vector, *temp_vector;
5561 LPWSTR p, q;
5562 DWORD sep_len;
5564 static const WCHAR separator[] = {'[','~',']',0};
5566 *numargs = 0;
5567 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5569 if (!args)
5570 return NULL;
5572 vector = msi_alloc(sizeof(LPWSTR));
5573 if (!vector)
5574 return NULL;
5576 p = args;
5579 (*numargs)++;
5580 vector[*numargs - 1] = p;
5582 if ((q = strstrW(p, separator)))
5584 *q = '\0';
5586 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5587 if (!temp_vector)
5589 msi_free(vector);
5590 return NULL;
5592 vector = temp_vector;
5594 p = q + sep_len;
5596 } while (q);
5598 return vector;
5601 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5603 MSIPACKAGE *package = param;
5604 MSICOMPONENT *comp;
5605 MSIRECORD *uirow;
5606 SC_HANDLE scm = NULL, service = NULL;
5607 LPCWSTR component, *vector = NULL;
5608 LPWSTR name, args, display_name = NULL;
5609 DWORD event, numargs, len, wait, dummy;
5610 UINT r = ERROR_FUNCTION_FAILED;
5611 SERVICE_STATUS_PROCESS status;
5612 ULONGLONG start_time;
5614 component = MSI_RecordGetString(rec, 6);
5615 comp = msi_get_loaded_component(package, component);
5616 if (!comp)
5617 return ERROR_SUCCESS;
5619 comp->Action = msi_get_component_action( package, comp );
5620 if (comp->Action != INSTALLSTATE_LOCAL)
5622 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5623 return ERROR_SUCCESS;
5626 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5627 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5628 event = MSI_RecordGetInteger(rec, 3);
5629 wait = MSI_RecordGetInteger(rec, 5);
5631 if (!(event & msidbServiceControlEventStart))
5633 r = ERROR_SUCCESS;
5634 goto done;
5637 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5638 if (!scm)
5640 ERR("Failed to open the service control manager\n");
5641 goto done;
5644 len = 0;
5645 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5646 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5648 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5649 GetServiceDisplayNameW( scm, name, display_name, &len );
5652 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5653 if (!service)
5655 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5656 goto done;
5659 vector = msi_service_args_to_vector(args, &numargs);
5661 if (!StartServiceW(service, numargs, vector) &&
5662 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5664 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5665 goto done;
5668 r = ERROR_SUCCESS;
5669 if (wait)
5671 /* wait for at most 30 seconds for the service to be up and running */
5672 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5673 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5675 TRACE("failed to query service status (%u)\n", GetLastError());
5676 goto done;
5678 start_time = GetTickCount64();
5679 while (status.dwCurrentState == SERVICE_START_PENDING)
5681 if (GetTickCount64() - start_time > 30000) break;
5682 Sleep(1000);
5683 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5684 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5686 TRACE("failed to query service status (%u)\n", GetLastError());
5687 goto done;
5690 if (status.dwCurrentState != SERVICE_RUNNING)
5692 WARN("service failed to start %u\n", status.dwCurrentState);
5693 r = ERROR_FUNCTION_FAILED;
5697 done:
5698 uirow = MSI_CreateRecord( 2 );
5699 MSI_RecordSetStringW( uirow, 1, display_name );
5700 MSI_RecordSetStringW( uirow, 2, name );
5701 msi_ui_actiondata( package, szStartServices, uirow );
5702 msiobj_release( &uirow->hdr );
5704 CloseServiceHandle(service);
5705 CloseServiceHandle(scm);
5707 msi_free(name);
5708 msi_free(args);
5709 msi_free(vector);
5710 msi_free(display_name);
5711 return r;
5714 static UINT ACTION_StartServices( MSIPACKAGE *package )
5716 static const WCHAR query[] = {
5717 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5718 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5719 MSIQUERY *view;
5720 UINT rc;
5722 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5723 if (rc != ERROR_SUCCESS)
5724 return ERROR_SUCCESS;
5726 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5727 msiobj_release(&view->hdr);
5728 return rc;
5731 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5733 DWORD i, needed, count;
5734 ENUM_SERVICE_STATUSW *dependencies;
5735 SERVICE_STATUS ss;
5736 SC_HANDLE depserv;
5738 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5739 0, &needed, &count))
5740 return TRUE;
5742 if (GetLastError() != ERROR_MORE_DATA)
5743 return FALSE;
5745 dependencies = msi_alloc(needed);
5746 if (!dependencies)
5747 return FALSE;
5749 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5750 needed, &needed, &count))
5751 goto error;
5753 for (i = 0; i < count; i++)
5755 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5756 SERVICE_STOP | SERVICE_QUERY_STATUS);
5757 if (!depserv)
5758 goto error;
5760 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5761 goto error;
5764 return TRUE;
5766 error:
5767 msi_free(dependencies);
5768 return FALSE;
5771 static UINT stop_service( LPCWSTR name )
5773 SC_HANDLE scm = NULL, service = NULL;
5774 SERVICE_STATUS status;
5775 SERVICE_STATUS_PROCESS ssp;
5776 DWORD needed;
5778 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5779 if (!scm)
5781 WARN("Failed to open the SCM: %d\n", GetLastError());
5782 goto done;
5785 service = OpenServiceW(scm, name,
5786 SERVICE_STOP |
5787 SERVICE_QUERY_STATUS |
5788 SERVICE_ENUMERATE_DEPENDENTS);
5789 if (!service)
5791 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5792 goto done;
5795 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5796 sizeof(SERVICE_STATUS_PROCESS), &needed))
5798 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5799 goto done;
5802 if (ssp.dwCurrentState == SERVICE_STOPPED)
5803 goto done;
5805 stop_service_dependents(scm, service);
5807 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5808 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5810 done:
5811 CloseServiceHandle(service);
5812 CloseServiceHandle(scm);
5814 return ERROR_SUCCESS;
5817 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5819 MSIPACKAGE *package = param;
5820 MSICOMPONENT *comp;
5821 MSIRECORD *uirow;
5822 LPCWSTR component;
5823 LPWSTR name = NULL, display_name = NULL;
5824 DWORD event, len;
5825 SC_HANDLE scm;
5827 event = MSI_RecordGetInteger( rec, 3 );
5828 if (!(event & msidbServiceControlEventStop))
5829 return ERROR_SUCCESS;
5831 component = MSI_RecordGetString( rec, 6 );
5832 comp = msi_get_loaded_component( package, component );
5833 if (!comp)
5834 return ERROR_SUCCESS;
5836 comp->Action = msi_get_component_action( package, comp );
5837 if (comp->Action != INSTALLSTATE_ABSENT)
5839 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5840 return ERROR_SUCCESS;
5843 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5844 if (!scm)
5846 ERR("Failed to open the service control manager\n");
5847 goto done;
5850 len = 0;
5851 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5852 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5854 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5855 GetServiceDisplayNameW( scm, name, display_name, &len );
5857 CloseServiceHandle( scm );
5859 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5860 stop_service( name );
5862 done:
5863 uirow = MSI_CreateRecord( 2 );
5864 MSI_RecordSetStringW( uirow, 1, display_name );
5865 MSI_RecordSetStringW( uirow, 2, name );
5866 msi_ui_actiondata( package, szStopServices, uirow );
5867 msiobj_release( &uirow->hdr );
5869 msi_free( name );
5870 msi_free( display_name );
5871 return ERROR_SUCCESS;
5874 static UINT ACTION_StopServices( MSIPACKAGE *package )
5876 static const WCHAR query[] = {
5877 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5878 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5879 MSIQUERY *view;
5880 UINT rc;
5882 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5883 if (rc != ERROR_SUCCESS)
5884 return ERROR_SUCCESS;
5886 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5887 msiobj_release(&view->hdr);
5888 return rc;
5891 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5893 MSIPACKAGE *package = param;
5894 MSICOMPONENT *comp;
5895 MSIRECORD *uirow;
5896 LPCWSTR component;
5897 LPWSTR name = NULL, display_name = NULL;
5898 DWORD event, len;
5899 SC_HANDLE scm = NULL, service = NULL;
5901 event = MSI_RecordGetInteger( rec, 3 );
5902 if (!(event & msidbServiceControlEventDelete))
5903 return ERROR_SUCCESS;
5905 component = MSI_RecordGetString(rec, 6);
5906 comp = msi_get_loaded_component(package, component);
5907 if (!comp)
5908 return ERROR_SUCCESS;
5910 comp->Action = msi_get_component_action( package, comp );
5911 if (comp->Action != INSTALLSTATE_ABSENT)
5913 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5914 return ERROR_SUCCESS;
5917 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5918 stop_service( name );
5920 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5921 if (!scm)
5923 WARN("Failed to open the SCM: %d\n", GetLastError());
5924 goto done;
5927 len = 0;
5928 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5929 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5931 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5932 GetServiceDisplayNameW( scm, name, display_name, &len );
5935 service = OpenServiceW( scm, name, DELETE );
5936 if (!service)
5938 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5939 goto done;
5942 if (!DeleteService( service ))
5943 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5945 done:
5946 uirow = MSI_CreateRecord( 2 );
5947 MSI_RecordSetStringW( uirow, 1, display_name );
5948 MSI_RecordSetStringW( uirow, 2, name );
5949 msi_ui_actiondata( package, szDeleteServices, uirow );
5950 msiobj_release( &uirow->hdr );
5952 CloseServiceHandle( service );
5953 CloseServiceHandle( scm );
5954 msi_free( name );
5955 msi_free( display_name );
5957 return ERROR_SUCCESS;
5960 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5962 static const WCHAR query[] = {
5963 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5964 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5965 MSIQUERY *view;
5966 UINT rc;
5968 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5969 if (rc != ERROR_SUCCESS)
5970 return ERROR_SUCCESS;
5972 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5973 msiobj_release( &view->hdr );
5974 return rc;
5977 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5979 MSIPACKAGE *package = param;
5980 LPWSTR driver, driver_path, ptr;
5981 WCHAR outpath[MAX_PATH];
5982 MSIFILE *driver_file = NULL, *setup_file = NULL;
5983 MSICOMPONENT *comp;
5984 MSIRECORD *uirow;
5985 LPCWSTR desc, file_key, component;
5986 DWORD len, usage;
5987 UINT r = ERROR_SUCCESS;
5989 static const WCHAR driver_fmt[] = {
5990 'D','r','i','v','e','r','=','%','s',0};
5991 static const WCHAR setup_fmt[] = {
5992 'S','e','t','u','p','=','%','s',0};
5993 static const WCHAR usage_fmt[] = {
5994 'F','i','l','e','U','s','a','g','e','=','1',0};
5996 component = MSI_RecordGetString( rec, 2 );
5997 comp = msi_get_loaded_component( package, component );
5998 if (!comp)
5999 return ERROR_SUCCESS;
6001 comp->Action = msi_get_component_action( package, comp );
6002 if (comp->Action != INSTALLSTATE_LOCAL)
6004 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6005 return ERROR_SUCCESS;
6007 desc = MSI_RecordGetString(rec, 3);
6009 file_key = MSI_RecordGetString( rec, 4 );
6010 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6012 file_key = MSI_RecordGetString( rec, 5 );
6013 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6015 if (!driver_file)
6017 ERR("ODBC Driver entry not found!\n");
6018 return ERROR_FUNCTION_FAILED;
6021 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6022 if (setup_file)
6023 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6024 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6026 driver = msi_alloc(len * sizeof(WCHAR));
6027 if (!driver)
6028 return ERROR_OUTOFMEMORY;
6030 ptr = driver;
6031 lstrcpyW(ptr, desc);
6032 ptr += lstrlenW(ptr) + 1;
6034 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6035 ptr += len + 1;
6037 if (setup_file)
6039 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6040 ptr += len + 1;
6043 lstrcpyW(ptr, usage_fmt);
6044 ptr += lstrlenW(ptr) + 1;
6045 *ptr = '\0';
6047 driver_path = strdupW(driver_file->TargetPath);
6048 ptr = strrchrW(driver_path, '\\');
6049 if (ptr) *ptr = '\0';
6051 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6052 NULL, ODBC_INSTALL_COMPLETE, &usage))
6054 ERR("Failed to install SQL driver!\n");
6055 r = ERROR_FUNCTION_FAILED;
6058 uirow = MSI_CreateRecord( 5 );
6059 MSI_RecordSetStringW( uirow, 1, desc );
6060 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6061 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6062 msi_ui_actiondata( package, szInstallODBC, uirow );
6063 msiobj_release( &uirow->hdr );
6065 msi_free(driver);
6066 msi_free(driver_path);
6068 return r;
6071 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6073 MSIPACKAGE *package = param;
6074 LPWSTR translator, translator_path, ptr;
6075 WCHAR outpath[MAX_PATH];
6076 MSIFILE *translator_file = NULL, *setup_file = NULL;
6077 MSICOMPONENT *comp;
6078 MSIRECORD *uirow;
6079 LPCWSTR desc, file_key, component;
6080 DWORD len, usage;
6081 UINT r = ERROR_SUCCESS;
6083 static const WCHAR translator_fmt[] = {
6084 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6085 static const WCHAR setup_fmt[] = {
6086 'S','e','t','u','p','=','%','s',0};
6088 component = MSI_RecordGetString( rec, 2 );
6089 comp = msi_get_loaded_component( package, component );
6090 if (!comp)
6091 return ERROR_SUCCESS;
6093 comp->Action = msi_get_component_action( package, comp );
6094 if (comp->Action != INSTALLSTATE_LOCAL)
6096 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6097 return ERROR_SUCCESS;
6099 desc = MSI_RecordGetString(rec, 3);
6101 file_key = MSI_RecordGetString( rec, 4 );
6102 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6104 file_key = MSI_RecordGetString( rec, 5 );
6105 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6107 if (!translator_file)
6109 ERR("ODBC Translator entry not found!\n");
6110 return ERROR_FUNCTION_FAILED;
6113 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6114 if (setup_file)
6115 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6117 translator = msi_alloc(len * sizeof(WCHAR));
6118 if (!translator)
6119 return ERROR_OUTOFMEMORY;
6121 ptr = translator;
6122 lstrcpyW(ptr, desc);
6123 ptr += lstrlenW(ptr) + 1;
6125 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6126 ptr += len + 1;
6128 if (setup_file)
6130 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6131 ptr += len + 1;
6133 *ptr = '\0';
6135 translator_path = strdupW(translator_file->TargetPath);
6136 ptr = strrchrW(translator_path, '\\');
6137 if (ptr) *ptr = '\0';
6139 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6140 NULL, ODBC_INSTALL_COMPLETE, &usage))
6142 ERR("Failed to install SQL translator!\n");
6143 r = ERROR_FUNCTION_FAILED;
6146 uirow = MSI_CreateRecord( 5 );
6147 MSI_RecordSetStringW( uirow, 1, desc );
6148 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6149 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6150 msi_ui_actiondata( package, szInstallODBC, uirow );
6151 msiobj_release( &uirow->hdr );
6153 msi_free(translator);
6154 msi_free(translator_path);
6156 return r;
6159 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6161 MSIPACKAGE *package = param;
6162 MSICOMPONENT *comp;
6163 LPWSTR attrs;
6164 LPCWSTR desc, driver, component;
6165 WORD request = ODBC_ADD_SYS_DSN;
6166 INT registration;
6167 DWORD len;
6168 UINT r = ERROR_SUCCESS;
6169 MSIRECORD *uirow;
6171 static const WCHAR attrs_fmt[] = {
6172 'D','S','N','=','%','s',0 };
6174 component = MSI_RecordGetString( rec, 2 );
6175 comp = msi_get_loaded_component( package, component );
6176 if (!comp)
6177 return ERROR_SUCCESS;
6179 comp->Action = msi_get_component_action( package, comp );
6180 if (comp->Action != INSTALLSTATE_LOCAL)
6182 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6183 return ERROR_SUCCESS;
6186 desc = MSI_RecordGetString(rec, 3);
6187 driver = MSI_RecordGetString(rec, 4);
6188 registration = MSI_RecordGetInteger(rec, 5);
6190 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6191 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6193 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6194 attrs = msi_alloc(len * sizeof(WCHAR));
6195 if (!attrs)
6196 return ERROR_OUTOFMEMORY;
6198 len = sprintfW(attrs, attrs_fmt, desc);
6199 attrs[len + 1] = 0;
6201 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6203 ERR("Failed to install SQL data source!\n");
6204 r = ERROR_FUNCTION_FAILED;
6207 uirow = MSI_CreateRecord( 5 );
6208 MSI_RecordSetStringW( uirow, 1, desc );
6209 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6210 MSI_RecordSetInteger( uirow, 3, request );
6211 msi_ui_actiondata( package, szInstallODBC, uirow );
6212 msiobj_release( &uirow->hdr );
6214 msi_free(attrs);
6216 return r;
6219 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6221 static const WCHAR driver_query[] = {
6222 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6223 'O','D','B','C','D','r','i','v','e','r',0};
6224 static const WCHAR translator_query[] = {
6225 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6226 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6227 static const WCHAR source_query[] = {
6228 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6229 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6230 MSIQUERY *view;
6231 UINT rc;
6233 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6234 if (rc == ERROR_SUCCESS)
6236 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6237 msiobj_release(&view->hdr);
6238 if (rc != ERROR_SUCCESS)
6239 return rc;
6241 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6242 if (rc == ERROR_SUCCESS)
6244 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6245 msiobj_release(&view->hdr);
6246 if (rc != ERROR_SUCCESS)
6247 return rc;
6249 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6250 if (rc == ERROR_SUCCESS)
6252 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6253 msiobj_release(&view->hdr);
6254 if (rc != ERROR_SUCCESS)
6255 return rc;
6257 return ERROR_SUCCESS;
6260 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6262 MSIPACKAGE *package = param;
6263 MSICOMPONENT *comp;
6264 MSIRECORD *uirow;
6265 DWORD usage;
6266 LPCWSTR desc, component;
6268 component = MSI_RecordGetString( rec, 2 );
6269 comp = msi_get_loaded_component( package, component );
6270 if (!comp)
6271 return ERROR_SUCCESS;
6273 comp->Action = msi_get_component_action( package, comp );
6274 if (comp->Action != INSTALLSTATE_ABSENT)
6276 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6277 return ERROR_SUCCESS;
6280 desc = MSI_RecordGetString( rec, 3 );
6281 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6283 WARN("Failed to remove ODBC driver\n");
6285 else if (!usage)
6287 FIXME("Usage count reached 0\n");
6290 uirow = MSI_CreateRecord( 2 );
6291 MSI_RecordSetStringW( uirow, 1, desc );
6292 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6293 msi_ui_actiondata( package, szRemoveODBC, uirow );
6294 msiobj_release( &uirow->hdr );
6296 return ERROR_SUCCESS;
6299 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6301 MSIPACKAGE *package = param;
6302 MSICOMPONENT *comp;
6303 MSIRECORD *uirow;
6304 DWORD usage;
6305 LPCWSTR desc, component;
6307 component = MSI_RecordGetString( rec, 2 );
6308 comp = msi_get_loaded_component( package, component );
6309 if (!comp)
6310 return ERROR_SUCCESS;
6312 comp->Action = msi_get_component_action( package, comp );
6313 if (comp->Action != INSTALLSTATE_ABSENT)
6315 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6316 return ERROR_SUCCESS;
6319 desc = MSI_RecordGetString( rec, 3 );
6320 if (!SQLRemoveTranslatorW( desc, &usage ))
6322 WARN("Failed to remove ODBC translator\n");
6324 else if (!usage)
6326 FIXME("Usage count reached 0\n");
6329 uirow = MSI_CreateRecord( 2 );
6330 MSI_RecordSetStringW( uirow, 1, desc );
6331 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6332 msi_ui_actiondata( package, szRemoveODBC, uirow );
6333 msiobj_release( &uirow->hdr );
6335 return ERROR_SUCCESS;
6338 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6340 MSIPACKAGE *package = param;
6341 MSICOMPONENT *comp;
6342 MSIRECORD *uirow;
6343 LPWSTR attrs;
6344 LPCWSTR desc, driver, component;
6345 WORD request = ODBC_REMOVE_SYS_DSN;
6346 INT registration;
6347 DWORD len;
6349 static const WCHAR attrs_fmt[] = {
6350 'D','S','N','=','%','s',0 };
6352 component = MSI_RecordGetString( rec, 2 );
6353 comp = msi_get_loaded_component( package, component );
6354 if (!comp)
6355 return ERROR_SUCCESS;
6357 comp->Action = msi_get_component_action( package, comp );
6358 if (comp->Action != INSTALLSTATE_ABSENT)
6360 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6361 return ERROR_SUCCESS;
6364 desc = MSI_RecordGetString( rec, 3 );
6365 driver = MSI_RecordGetString( rec, 4 );
6366 registration = MSI_RecordGetInteger( rec, 5 );
6368 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6369 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6371 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6372 attrs = msi_alloc( len * sizeof(WCHAR) );
6373 if (!attrs)
6374 return ERROR_OUTOFMEMORY;
6376 FIXME("Use ODBCSourceAttribute table\n");
6378 len = sprintfW( attrs, attrs_fmt, desc );
6379 attrs[len + 1] = 0;
6381 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6383 WARN("Failed to remove ODBC data source\n");
6385 msi_free( attrs );
6387 uirow = MSI_CreateRecord( 3 );
6388 MSI_RecordSetStringW( uirow, 1, desc );
6389 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6390 MSI_RecordSetInteger( uirow, 3, request );
6391 msi_ui_actiondata( package, szRemoveODBC, uirow );
6392 msiobj_release( &uirow->hdr );
6394 return ERROR_SUCCESS;
6397 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6399 static const WCHAR driver_query[] = {
6400 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6401 'O','D','B','C','D','r','i','v','e','r',0};
6402 static const WCHAR translator_query[] = {
6403 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6404 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6405 static const WCHAR source_query[] = {
6406 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6407 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6408 MSIQUERY *view;
6409 UINT rc;
6411 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6412 if (rc == ERROR_SUCCESS)
6414 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6415 msiobj_release( &view->hdr );
6416 if (rc != ERROR_SUCCESS)
6417 return rc;
6419 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6420 if (rc == ERROR_SUCCESS)
6422 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6423 msiobj_release( &view->hdr );
6424 if (rc != ERROR_SUCCESS)
6425 return rc;
6427 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6428 if (rc == ERROR_SUCCESS)
6430 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6431 msiobj_release( &view->hdr );
6432 if (rc != ERROR_SUCCESS)
6433 return rc;
6435 return ERROR_SUCCESS;
6438 #define ENV_ACT_SETALWAYS 0x1
6439 #define ENV_ACT_SETABSENT 0x2
6440 #define ENV_ACT_REMOVE 0x4
6441 #define ENV_ACT_REMOVEMATCH 0x8
6443 #define ENV_MOD_MACHINE 0x20000000
6444 #define ENV_MOD_APPEND 0x40000000
6445 #define ENV_MOD_PREFIX 0x80000000
6446 #define ENV_MOD_MASK 0xC0000000
6448 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6450 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6452 LPCWSTR cptr = *name;
6454 static const WCHAR prefix[] = {'[','~',']',0};
6455 static const int prefix_len = 3;
6457 *flags = 0;
6458 while (*cptr)
6460 if (*cptr == '=')
6461 *flags |= ENV_ACT_SETALWAYS;
6462 else if (*cptr == '+')
6463 *flags |= ENV_ACT_SETABSENT;
6464 else if (*cptr == '-')
6465 *flags |= ENV_ACT_REMOVE;
6466 else if (*cptr == '!')
6467 *flags |= ENV_ACT_REMOVEMATCH;
6468 else if (*cptr == '*')
6469 *flags |= ENV_MOD_MACHINE;
6470 else
6471 break;
6473 cptr++;
6474 (*name)++;
6477 if (!*cptr)
6479 ERR("Missing environment variable\n");
6480 return ERROR_FUNCTION_FAILED;
6483 if (*value)
6485 LPCWSTR ptr = *value;
6486 if (!strncmpW(ptr, prefix, prefix_len))
6488 if (ptr[prefix_len] == szSemiColon[0])
6490 *flags |= ENV_MOD_APPEND;
6491 *value += lstrlenW(prefix);
6493 else
6495 *value = NULL;
6498 else if (lstrlenW(*value) >= prefix_len)
6500 ptr += lstrlenW(ptr) - prefix_len;
6501 if (!strcmpW( ptr, prefix ))
6503 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6505 *flags |= ENV_MOD_PREFIX;
6506 /* the "[~]" will be removed by deformat_string */;
6508 else
6510 *value = NULL;
6516 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6517 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6518 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6519 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6521 ERR("Invalid flags: %08x\n", *flags);
6522 return ERROR_FUNCTION_FAILED;
6525 if (!*flags)
6526 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6528 return ERROR_SUCCESS;
6531 static UINT open_env_key( DWORD flags, HKEY *key )
6533 static const WCHAR user_env[] =
6534 {'E','n','v','i','r','o','n','m','e','n','t',0};
6535 static const WCHAR machine_env[] =
6536 {'S','y','s','t','e','m','\\',
6537 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6538 'C','o','n','t','r','o','l','\\',
6539 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6540 'E','n','v','i','r','o','n','m','e','n','t',0};
6541 const WCHAR *env;
6542 HKEY root;
6543 LONG res;
6545 if (flags & ENV_MOD_MACHINE)
6547 env = machine_env;
6548 root = HKEY_LOCAL_MACHINE;
6550 else
6552 env = user_env;
6553 root = HKEY_CURRENT_USER;
6556 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6557 if (res != ERROR_SUCCESS)
6559 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6560 return ERROR_FUNCTION_FAILED;
6563 return ERROR_SUCCESS;
6566 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6568 MSIPACKAGE *package = param;
6569 LPCWSTR name, value, component;
6570 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6571 DWORD flags, type, size;
6572 UINT res;
6573 HKEY env = NULL;
6574 MSICOMPONENT *comp;
6575 MSIRECORD *uirow;
6576 int action = 0;
6578 component = MSI_RecordGetString(rec, 4);
6579 comp = msi_get_loaded_component(package, component);
6580 if (!comp)
6581 return ERROR_SUCCESS;
6583 comp->Action = msi_get_component_action( package, comp );
6584 if (comp->Action != INSTALLSTATE_LOCAL)
6586 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6587 return ERROR_SUCCESS;
6589 name = MSI_RecordGetString(rec, 2);
6590 value = MSI_RecordGetString(rec, 3);
6592 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6594 res = env_parse_flags(&name, &value, &flags);
6595 if (res != ERROR_SUCCESS || !value)
6596 goto done;
6598 if (value && !deformat_string(package, value, &deformatted))
6600 res = ERROR_OUTOFMEMORY;
6601 goto done;
6604 value = deformatted;
6606 res = open_env_key( flags, &env );
6607 if (res != ERROR_SUCCESS)
6608 goto done;
6610 if (flags & ENV_MOD_MACHINE)
6611 action |= 0x20000000;
6613 size = 0;
6614 type = REG_SZ;
6615 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6616 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6617 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6618 goto done;
6620 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6622 action = 0x2;
6624 /* Nothing to do. */
6625 if (!value)
6627 res = ERROR_SUCCESS;
6628 goto done;
6631 /* If we are appending but the string was empty, strip ; */
6632 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6634 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6635 newval = strdupW(value);
6636 if (!newval)
6638 res = ERROR_OUTOFMEMORY;
6639 goto done;
6642 else
6644 action = 0x1;
6646 /* Contrary to MSDN, +-variable to [~];path works */
6647 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6649 res = ERROR_SUCCESS;
6650 goto done;
6653 data = msi_alloc(size);
6654 if (!data)
6656 RegCloseKey(env);
6657 return ERROR_OUTOFMEMORY;
6660 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6661 if (res != ERROR_SUCCESS)
6662 goto done;
6664 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6666 action = 0x4;
6667 res = RegDeleteValueW(env, name);
6668 if (res != ERROR_SUCCESS)
6669 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6670 goto done;
6673 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6674 if (flags & ENV_MOD_MASK)
6676 DWORD mod_size;
6677 int multiplier = 0;
6678 if (flags & ENV_MOD_APPEND) multiplier++;
6679 if (flags & ENV_MOD_PREFIX) multiplier++;
6680 mod_size = lstrlenW(value) * multiplier;
6681 size += mod_size * sizeof(WCHAR);
6684 newval = msi_alloc(size);
6685 ptr = newval;
6686 if (!newval)
6688 res = ERROR_OUTOFMEMORY;
6689 goto done;
6692 if (flags & ENV_MOD_PREFIX)
6694 lstrcpyW(newval, value);
6695 ptr = newval + lstrlenW(value);
6696 action |= 0x80000000;
6699 lstrcpyW(ptr, data);
6701 if (flags & ENV_MOD_APPEND)
6703 lstrcatW(newval, value);
6704 action |= 0x40000000;
6707 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6708 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6709 if (res)
6711 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6714 done:
6715 uirow = MSI_CreateRecord( 3 );
6716 MSI_RecordSetStringW( uirow, 1, name );
6717 MSI_RecordSetStringW( uirow, 2, newval );
6718 MSI_RecordSetInteger( uirow, 3, action );
6719 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6720 msiobj_release( &uirow->hdr );
6722 if (env) RegCloseKey(env);
6723 msi_free(deformatted);
6724 msi_free(data);
6725 msi_free(newval);
6726 return res;
6729 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6731 static const WCHAR query[] = {
6732 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6733 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6734 MSIQUERY *view;
6735 UINT rc;
6737 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6738 if (rc != ERROR_SUCCESS)
6739 return ERROR_SUCCESS;
6741 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6742 msiobj_release(&view->hdr);
6743 return rc;
6746 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6748 MSIPACKAGE *package = param;
6749 LPCWSTR name, value, component;
6750 LPWSTR deformatted = NULL;
6751 DWORD flags;
6752 HKEY env;
6753 MSICOMPONENT *comp;
6754 MSIRECORD *uirow;
6755 int action = 0;
6756 LONG res;
6757 UINT r;
6759 component = MSI_RecordGetString( rec, 4 );
6760 comp = msi_get_loaded_component( package, component );
6761 if (!comp)
6762 return ERROR_SUCCESS;
6764 comp->Action = msi_get_component_action( package, comp );
6765 if (comp->Action != INSTALLSTATE_ABSENT)
6767 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6768 return ERROR_SUCCESS;
6770 name = MSI_RecordGetString( rec, 2 );
6771 value = MSI_RecordGetString( rec, 3 );
6773 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6775 r = env_parse_flags( &name, &value, &flags );
6776 if (r != ERROR_SUCCESS)
6777 return r;
6779 if (!(flags & ENV_ACT_REMOVE))
6781 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6782 return ERROR_SUCCESS;
6785 if (value && !deformat_string( package, value, &deformatted ))
6786 return ERROR_OUTOFMEMORY;
6788 value = deformatted;
6790 r = open_env_key( flags, &env );
6791 if (r != ERROR_SUCCESS)
6793 r = ERROR_SUCCESS;
6794 goto done;
6797 if (flags & ENV_MOD_MACHINE)
6798 action |= 0x20000000;
6800 TRACE("Removing %s\n", debugstr_w(name));
6802 res = RegDeleteValueW( env, name );
6803 if (res != ERROR_SUCCESS)
6805 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6806 r = ERROR_SUCCESS;
6809 done:
6810 uirow = MSI_CreateRecord( 3 );
6811 MSI_RecordSetStringW( uirow, 1, name );
6812 MSI_RecordSetStringW( uirow, 2, value );
6813 MSI_RecordSetInteger( uirow, 3, action );
6814 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6815 msiobj_release( &uirow->hdr );
6817 if (env) RegCloseKey( env );
6818 msi_free( deformatted );
6819 return r;
6822 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6824 static const WCHAR query[] = {
6825 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6826 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6827 MSIQUERY *view;
6828 UINT rc;
6830 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6831 if (rc != ERROR_SUCCESS)
6832 return ERROR_SUCCESS;
6834 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6835 msiobj_release( &view->hdr );
6836 return rc;
6839 UINT msi_validate_product_id( MSIPACKAGE *package )
6841 LPWSTR key, template, id;
6842 UINT r = ERROR_SUCCESS;
6844 id = msi_dup_property( package->db, szProductID );
6845 if (id)
6847 msi_free( id );
6848 return ERROR_SUCCESS;
6850 template = msi_dup_property( package->db, szPIDTemplate );
6851 key = msi_dup_property( package->db, szPIDKEY );
6852 if (key && template)
6854 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6855 r = msi_set_property( package->db, szProductID, key );
6857 msi_free( template );
6858 msi_free( key );
6859 return r;
6862 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6864 return msi_validate_product_id( package );
6867 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6869 TRACE("\n");
6870 package->need_reboot = 1;
6871 return ERROR_SUCCESS;
6874 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6876 static const WCHAR szAvailableFreeReg[] =
6877 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6878 MSIRECORD *uirow;
6879 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6881 TRACE("%p %d kilobytes\n", package, space);
6883 uirow = MSI_CreateRecord( 1 );
6884 MSI_RecordSetInteger( uirow, 1, space );
6885 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6886 msiobj_release( &uirow->hdr );
6888 return ERROR_SUCCESS;
6891 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6893 TRACE("%p\n", package);
6895 msi_set_property( package->db, szRollbackDisabled, szOne );
6896 return ERROR_SUCCESS;
6899 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6901 FIXME("%p\n", package);
6902 return ERROR_SUCCESS;
6905 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6907 static const WCHAR driver_query[] = {
6908 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6909 'O','D','B','C','D','r','i','v','e','r',0};
6910 static const WCHAR translator_query[] = {
6911 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6912 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6913 MSIQUERY *view;
6914 UINT r, count;
6916 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6917 if (r == ERROR_SUCCESS)
6919 count = 0;
6920 r = MSI_IterateRecords( view, &count, NULL, package );
6921 msiobj_release( &view->hdr );
6922 if (r != ERROR_SUCCESS)
6923 return r;
6924 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6926 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6927 if (r == ERROR_SUCCESS)
6929 count = 0;
6930 r = MSI_IterateRecords( view, &count, NULL, package );
6931 msiobj_release( &view->hdr );
6932 if (r != ERROR_SUCCESS)
6933 return r;
6934 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6936 return ERROR_SUCCESS;
6939 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6941 MSIPACKAGE *package = param;
6942 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6943 WCHAR *value;
6945 if ((value = msi_dup_property( package->db, property )))
6947 FIXME("remove %s\n", debugstr_w(value));
6948 msi_free( value );
6950 return ERROR_SUCCESS;
6953 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6955 static const WCHAR query[] = {
6956 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6957 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6958 MSIQUERY *view;
6959 UINT r;
6961 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6962 if (r == ERROR_SUCCESS)
6964 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6965 msiobj_release( &view->hdr );
6966 if (r != ERROR_SUCCESS)
6967 return r;
6969 return ERROR_SUCCESS;
6972 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6974 MSIPACKAGE *package = param;
6975 int attributes = MSI_RecordGetInteger( rec, 5 );
6977 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6979 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6980 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6981 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6982 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6983 HKEY hkey;
6984 UINT r;
6986 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
6988 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6989 if (r != ERROR_SUCCESS)
6990 return ERROR_SUCCESS;
6992 else
6994 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
6995 if (r != ERROR_SUCCESS)
6996 return ERROR_SUCCESS;
6998 RegCloseKey( hkey );
7000 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7001 debugstr_w(upgrade_code), debugstr_w(version_min),
7002 debugstr_w(version_max), debugstr_w(language));
7004 return ERROR_SUCCESS;
7007 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7009 static const WCHAR query[] = {
7010 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7011 'U','p','g','r','a','d','e',0};
7012 MSIQUERY *view;
7013 UINT r;
7015 if (msi_get_property_int( package->db, szInstalled, 0 ))
7017 TRACE("product is installed, skipping action\n");
7018 return ERROR_SUCCESS;
7020 if (msi_get_property_int( package->db, szPreselected, 0 ))
7022 TRACE("Preselected property is set, not migrating feature states\n");
7023 return ERROR_SUCCESS;
7025 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7026 if (r == ERROR_SUCCESS)
7028 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7029 msiobj_release( &view->hdr );
7030 if (r != ERROR_SUCCESS)
7031 return r;
7033 return ERROR_SUCCESS;
7036 static void bind_image( const char *filename, const char *path )
7038 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7040 WARN("failed to bind image %u\n", GetLastError());
7044 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7046 UINT i;
7047 MSIFILE *file;
7048 MSIPACKAGE *package = param;
7049 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7050 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7051 char *filenameA, *pathA;
7052 WCHAR *pathW, **path_list;
7054 if (!(file = msi_get_loaded_file( package, key )))
7056 WARN("file %s not found\n", debugstr_w(key));
7057 return ERROR_SUCCESS;
7059 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7060 path_list = msi_split_string( paths, ';' );
7061 if (!path_list) bind_image( filenameA, NULL );
7062 else
7064 for (i = 0; path_list[i] && path_list[i][0]; i++)
7066 deformat_string( package, path_list[i], &pathW );
7067 if ((pathA = strdupWtoA( pathW )))
7069 bind_image( filenameA, pathA );
7070 msi_free( pathA );
7072 msi_free( pathW );
7075 msi_free( path_list );
7076 msi_free( filenameA );
7077 return ERROR_SUCCESS;
7080 static UINT ACTION_BindImage( MSIPACKAGE *package )
7082 static const WCHAR query[] = {
7083 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7084 'B','i','n','d','I','m','a','g','e',0};
7085 MSIQUERY *view;
7086 UINT r;
7088 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7089 if (r == ERROR_SUCCESS)
7091 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7092 msiobj_release( &view->hdr );
7093 if (r != ERROR_SUCCESS)
7094 return r;
7096 return ERROR_SUCCESS;
7099 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7101 static const WCHAR query[] = {
7102 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7103 MSIQUERY *view;
7104 DWORD count = 0;
7105 UINT r;
7107 r = MSI_OpenQuery( package->db, &view, query, table );
7108 if (r == ERROR_SUCCESS)
7110 r = MSI_IterateRecords(view, &count, NULL, package);
7111 msiobj_release(&view->hdr);
7112 if (r != ERROR_SUCCESS)
7113 return r;
7115 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7116 return ERROR_SUCCESS;
7119 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7121 static const WCHAR table[] = {
7122 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7123 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7126 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7128 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7129 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7132 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7134 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7135 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7138 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7140 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7141 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7144 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7146 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7147 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7150 static const struct
7152 const WCHAR *action;
7153 UINT (*handler)(MSIPACKAGE *);
7154 const WCHAR *action_rollback;
7156 StandardActions[] =
7158 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7159 { szAppSearch, ACTION_AppSearch, NULL },
7160 { szBindImage, ACTION_BindImage, NULL },
7161 { szCCPSearch, ACTION_CCPSearch, NULL },
7162 { szCostFinalize, ACTION_CostFinalize, NULL },
7163 { szCostInitialize, ACTION_CostInitialize, NULL },
7164 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7165 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7166 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7167 { szDisableRollback, ACTION_DisableRollback, NULL },
7168 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7169 { szExecuteAction, ACTION_ExecuteAction, NULL },
7170 { szFileCost, ACTION_FileCost, NULL },
7171 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7172 { szForceReboot, ACTION_ForceReboot, NULL },
7173 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7174 { szInstallExecute, ACTION_InstallExecute, NULL },
7175 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7176 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7177 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7178 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7179 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7180 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7181 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7182 { szInstallValidate, ACTION_InstallValidate, NULL },
7183 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7184 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7185 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7186 { szMoveFiles, ACTION_MoveFiles, NULL },
7187 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7188 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7189 { szPatchFiles, ACTION_PatchFiles, NULL },
7190 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7191 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7192 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7193 { szPublishProduct, ACTION_PublishProduct, NULL },
7194 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7195 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7196 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7197 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7198 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7199 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7200 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7201 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7202 { szRegisterUser, ACTION_RegisterUser, NULL },
7203 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7204 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7205 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7206 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7207 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7208 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7209 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7210 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7211 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7212 { szResolveSource, ACTION_ResolveSource, NULL },
7213 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7214 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7215 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7216 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7217 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7218 { szStartServices, ACTION_StartServices, szStopServices },
7219 { szStopServices, ACTION_StopServices, szStartServices },
7220 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7221 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7222 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7223 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7224 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7225 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7226 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7227 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7228 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7229 { szValidateProductID, ACTION_ValidateProductID, NULL },
7230 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7231 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7232 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7233 { NULL, NULL, NULL }
7236 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7238 BOOL ret = FALSE;
7239 UINT i;
7241 i = 0;
7242 while (StandardActions[i].action != NULL)
7244 if (!strcmpW( StandardActions[i].action, action ))
7246 ui_actionstart( package, action );
7247 if (StandardActions[i].handler)
7249 ui_actioninfo( package, action, TRUE, 0 );
7250 *rc = StandardActions[i].handler( package );
7251 ui_actioninfo( package, action, FALSE, *rc );
7253 if (StandardActions[i].action_rollback && !package->need_rollback)
7255 TRACE("scheduling rollback action\n");
7256 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7259 else
7261 FIXME("unhandled standard action %s\n", debugstr_w(action));
7262 *rc = ERROR_SUCCESS;
7264 ret = TRUE;
7265 break;
7267 i++;
7269 return ret;
7272 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7274 UINT rc = ERROR_SUCCESS;
7275 BOOL handled;
7277 TRACE("Performing action (%s)\n", debugstr_w(action));
7279 handled = ACTION_HandleStandardAction(package, action, &rc);
7281 if (!handled)
7282 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7284 if (!handled)
7286 WARN("unhandled msi action %s\n", debugstr_w(action));
7287 rc = ERROR_FUNCTION_NOT_CALLED;
7290 return rc;
7293 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7295 UINT rc = ERROR_SUCCESS;
7296 BOOL handled = FALSE;
7298 TRACE("Performing action (%s)\n", debugstr_w(action));
7300 handled = ACTION_HandleStandardAction(package, action, &rc);
7302 if (!handled)
7303 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7305 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7306 handled = TRUE;
7308 if (!handled)
7310 WARN("unhandled msi action %s\n", debugstr_w(action));
7311 rc = ERROR_FUNCTION_NOT_CALLED;
7314 return rc;
7317 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7319 UINT rc = ERROR_SUCCESS;
7320 MSIRECORD *row;
7322 static const WCHAR query[] =
7323 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7324 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7325 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7326 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7327 static const WCHAR ui_query[] =
7328 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7329 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7330 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7331 ' ', '=',' ','%','i',0};
7333 if (needs_ui_sequence(package))
7334 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7335 else
7336 row = MSI_QueryGetRecord(package->db, query, seq);
7338 if (row)
7340 LPCWSTR action, cond;
7342 TRACE("Running the actions\n");
7344 /* check conditions */
7345 cond = MSI_RecordGetString(row, 2);
7347 /* this is a hack to skip errors in the condition code */
7348 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7350 msiobj_release(&row->hdr);
7351 return ERROR_SUCCESS;
7354 action = MSI_RecordGetString(row, 1);
7355 if (!action)
7357 ERR("failed to fetch action\n");
7358 msiobj_release(&row->hdr);
7359 return ERROR_FUNCTION_FAILED;
7362 if (needs_ui_sequence(package))
7363 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7364 else
7365 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7367 msiobj_release(&row->hdr);
7370 return rc;
7373 /****************************************************
7374 * TOP level entry points
7375 *****************************************************/
7377 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7378 LPCWSTR szCommandLine )
7380 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7381 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7382 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7383 WCHAR *reinstall = NULL;
7384 BOOL ui_exists;
7385 UINT rc;
7387 msi_set_property( package->db, szAction, szInstall );
7389 package->script->InWhatSequence = SEQUENCE_INSTALL;
7391 if (szPackagePath)
7393 LPWSTR p, dir;
7394 LPCWSTR file;
7396 dir = strdupW(szPackagePath);
7397 p = strrchrW(dir, '\\');
7398 if (p)
7400 *(++p) = 0;
7401 file = szPackagePath + (p - dir);
7403 else
7405 msi_free(dir);
7406 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7407 GetCurrentDirectoryW(MAX_PATH, dir);
7408 lstrcatW(dir, szBackSlash);
7409 file = szPackagePath;
7412 msi_free( package->PackagePath );
7413 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7414 if (!package->PackagePath)
7416 msi_free(dir);
7417 return ERROR_OUTOFMEMORY;
7420 lstrcpyW(package->PackagePath, dir);
7421 lstrcatW(package->PackagePath, file);
7422 msi_free(dir);
7424 msi_set_sourcedir_props(package, FALSE);
7427 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7428 if (rc != ERROR_SUCCESS)
7429 return rc;
7431 msi_apply_transforms( package );
7432 msi_apply_patches( package );
7434 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7436 TRACE("setting reinstall property\n");
7437 msi_set_property( package->db, szReinstall, szAll );
7440 /* properties may have been added by a transform */
7441 msi_clone_properties( package );
7443 msi_parse_command_line( package, szCommandLine, FALSE );
7444 msi_adjust_privilege_properties( package );
7445 msi_set_context( package );
7447 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7449 TRACE("disabling rollback\n");
7450 msi_set_property( package->db, szRollbackDisabled, szOne );
7453 if (needs_ui_sequence( package))
7455 package->script->InWhatSequence |= SEQUENCE_UI;
7456 rc = ACTION_ProcessUISequence(package);
7457 ui_exists = ui_sequence_exists(package);
7458 if (rc == ERROR_SUCCESS || !ui_exists)
7460 package->script->InWhatSequence |= SEQUENCE_EXEC;
7461 rc = ACTION_ProcessExecSequence(package, ui_exists);
7464 else
7465 rc = ACTION_ProcessExecSequence(package, FALSE);
7467 package->script->CurrentlyScripting = FALSE;
7469 /* process the ending type action */
7470 if (rc == ERROR_SUCCESS)
7471 ACTION_PerformActionSequence(package, -1);
7472 else if (rc == ERROR_INSTALL_USEREXIT)
7473 ACTION_PerformActionSequence(package, -2);
7474 else if (rc == ERROR_INSTALL_SUSPEND)
7475 ACTION_PerformActionSequence(package, -4);
7476 else /* failed */
7478 ACTION_PerformActionSequence(package, -3);
7479 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7481 package->need_rollback = TRUE;
7485 /* finish up running custom actions */
7486 ACTION_FinishCustomActions(package);
7488 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7490 WARN("installation failed, running rollback script\n");
7491 execute_script( package, SCRIPT_ROLLBACK );
7493 msi_free( reinstall );
7495 if (rc == ERROR_SUCCESS && package->need_reboot)
7496 return ERROR_SUCCESS_REBOOT_REQUIRED;
7498 return rc;