Release 1.4-rc3.
[wine/multimedia.git] / dlls / msi / action.c
blob87fc90690604b0328e722fe26235da97150b39a8
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 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1797 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1798 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1799 fl->feature->Action = feature->Action;
1800 fl->feature->ActionRequest = feature->ActionRequest;
1805 else /* preselected */
1807 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1809 if (!is_feature_selected( feature, level )) continue;
1811 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1813 if (feature->Installed == INSTALLSTATE_ABSENT)
1815 feature->Action = INSTALLSTATE_UNKNOWN;
1816 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1818 else
1820 feature->Action = feature->Installed;
1821 feature->ActionRequest = feature->Installed;
1825 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1827 FeatureList *fl;
1829 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1831 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1832 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1834 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1835 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1836 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1837 fl->feature->Action = feature->Action;
1838 fl->feature->ActionRequest = feature->ActionRequest;
1844 /* now we want to set component state based based on feature state */
1845 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1847 ComponentList *cl;
1849 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1850 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1851 feature->ActionRequest, feature->Action);
1853 if (!is_feature_selected( feature, level )) continue;
1855 /* features with components that have compressed files are made local */
1856 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1858 if (cl->component->ForceLocalState &&
1859 feature->ActionRequest == INSTALLSTATE_SOURCE)
1861 feature->Action = INSTALLSTATE_LOCAL;
1862 feature->ActionRequest = INSTALLSTATE_LOCAL;
1863 break;
1867 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1869 component = cl->component;
1871 switch (feature->ActionRequest)
1873 case INSTALLSTATE_ABSENT:
1874 component->anyAbsent = 1;
1875 break;
1876 case INSTALLSTATE_ADVERTISED:
1877 component->hasAdvertiseFeature = 1;
1878 break;
1879 case INSTALLSTATE_SOURCE:
1880 component->hasSourceFeature = 1;
1881 break;
1882 case INSTALLSTATE_LOCAL:
1883 component->hasLocalFeature = 1;
1884 break;
1885 case INSTALLSTATE_DEFAULT:
1886 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1887 component->hasAdvertiseFeature = 1;
1888 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1889 component->hasSourceFeature = 1;
1890 else
1891 component->hasLocalFeature = 1;
1892 break;
1893 default:
1894 break;
1899 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1901 /* check if it's local or source */
1902 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1903 (component->hasLocalFeature || component->hasSourceFeature))
1905 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1906 !component->ForceLocalState)
1908 component->Action = INSTALLSTATE_SOURCE;
1909 component->ActionRequest = INSTALLSTATE_SOURCE;
1911 else
1913 component->Action = INSTALLSTATE_LOCAL;
1914 component->ActionRequest = INSTALLSTATE_LOCAL;
1916 continue;
1919 /* if any feature is local, the component must be local too */
1920 if (component->hasLocalFeature)
1922 component->Action = INSTALLSTATE_LOCAL;
1923 component->ActionRequest = INSTALLSTATE_LOCAL;
1924 continue;
1926 if (component->hasSourceFeature)
1928 component->Action = INSTALLSTATE_SOURCE;
1929 component->ActionRequest = INSTALLSTATE_SOURCE;
1930 continue;
1932 if (component->hasAdvertiseFeature)
1934 component->Action = INSTALLSTATE_ADVERTISED;
1935 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1936 continue;
1938 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1939 if (component->anyAbsent &&
1940 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1942 component->Action = INSTALLSTATE_ABSENT;
1943 component->ActionRequest = INSTALLSTATE_ABSENT;
1947 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1949 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1951 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1952 component->Action = INSTALLSTATE_LOCAL;
1953 component->ActionRequest = INSTALLSTATE_LOCAL;
1956 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1957 component->Installed == INSTALLSTATE_SOURCE &&
1958 component->hasSourceFeature)
1960 component->Action = INSTALLSTATE_UNKNOWN;
1961 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1964 TRACE("component %s (installed %d request %d action %d)\n",
1965 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1968 return ERROR_SUCCESS;
1971 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1973 MSIPACKAGE *package = param;
1974 LPCWSTR name;
1975 MSIFEATURE *feature;
1977 name = MSI_RecordGetString( row, 1 );
1979 feature = msi_get_loaded_feature( package, name );
1980 if (!feature)
1981 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1982 else
1984 LPCWSTR Condition;
1985 Condition = MSI_RecordGetString(row,3);
1987 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1989 int level = MSI_RecordGetInteger(row,2);
1990 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1991 feature->Level = level;
1994 return ERROR_SUCCESS;
1997 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1999 static const WCHAR name[] = {'\\',0};
2000 VS_FIXEDFILEINFO *ptr, *ret;
2001 LPVOID version;
2002 DWORD versize, handle;
2003 UINT sz;
2005 versize = GetFileVersionInfoSizeW( filename, &handle );
2006 if (!versize)
2007 return NULL;
2009 version = msi_alloc( versize );
2010 if (!version)
2011 return NULL;
2013 GetFileVersionInfoW( filename, 0, versize, version );
2015 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2017 msi_free( version );
2018 return NULL;
2021 ret = msi_alloc( sz );
2022 memcpy( ret, ptr, sz );
2024 msi_free( version );
2025 return ret;
2028 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2030 DWORD ms, ls;
2032 msi_parse_version_string( version, &ms, &ls );
2034 if (fi->dwFileVersionMS > ms) return 1;
2035 else if (fi->dwFileVersionMS < ms) return -1;
2036 else if (fi->dwFileVersionLS > ls) return 1;
2037 else if (fi->dwFileVersionLS < ls) return -1;
2038 return 0;
2041 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2043 DWORD ms1, ms2;
2045 msi_parse_version_string( ver1, &ms1, NULL );
2046 msi_parse_version_string( ver2, &ms2, NULL );
2048 if (ms1 > ms2) return 1;
2049 else if (ms1 < ms2) return -1;
2050 return 0;
2053 DWORD msi_get_disk_file_size( LPCWSTR filename )
2055 HANDLE file;
2056 DWORD size;
2058 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2059 if (file == INVALID_HANDLE_VALUE)
2060 return INVALID_FILE_SIZE;
2062 size = GetFileSize( file, NULL );
2063 TRACE("size is %u\n", size);
2064 CloseHandle( file );
2065 return size;
2068 BOOL msi_file_hash_matches( MSIFILE *file )
2070 UINT r;
2071 MSIFILEHASHINFO hash;
2073 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2074 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2075 if (r != ERROR_SUCCESS)
2076 return FALSE;
2078 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2081 static WCHAR *get_temp_dir( void )
2083 static UINT id;
2084 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2086 GetTempPathW( MAX_PATH, tmp );
2087 for (;;)
2089 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2090 if (CreateDirectoryW( dir, NULL )) break;
2092 return strdupW( dir );
2096 * msi_build_directory_name()
2098 * This function is to save messing round with directory names
2099 * It handles adding backslashes between path segments,
2100 * and can add \ at the end of the directory name if told to.
2102 * It takes a variable number of arguments.
2103 * It always allocates a new string for the result, so make sure
2104 * to free the return value when finished with it.
2106 * The first arg is the number of path segments that follow.
2107 * The arguments following count are a list of path segments.
2108 * A path segment may be NULL.
2110 * Path segments will be added with a \ separating them.
2111 * A \ will not be added after the last segment, however if the
2112 * last segment is NULL, then the last character will be a \
2114 WCHAR *msi_build_directory_name( DWORD count, ... )
2116 DWORD sz = 1, i;
2117 WCHAR *dir;
2118 va_list va;
2120 va_start( va, count );
2121 for (i = 0; i < count; i++)
2123 const WCHAR *str = va_arg( va, const WCHAR * );
2124 if (str) sz += strlenW( str ) + 1;
2126 va_end( va );
2128 dir = msi_alloc( sz * sizeof(WCHAR) );
2129 dir[0] = 0;
2131 va_start( va, count );
2132 for (i = 0; i < count; i++)
2134 const WCHAR *str = va_arg( va, const WCHAR * );
2135 if (!str) continue;
2136 strcatW( dir, str );
2137 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2139 va_end( va );
2140 return dir;
2143 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2145 MSIASSEMBLY *assembly = file->Component->assembly;
2147 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2149 msi_free( file->TargetPath );
2150 if (assembly && !assembly->application)
2152 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2153 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2154 msi_track_tempfile( package, file->TargetPath );
2156 else
2158 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2159 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2162 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2165 static UINT calculate_file_cost( MSIPACKAGE *package )
2167 VS_FIXEDFILEINFO *file_version;
2168 WCHAR *font_version;
2169 MSIFILE *file;
2171 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2173 MSICOMPONENT *comp = file->Component;
2174 DWORD file_size;
2176 if (!comp->Enabled) continue;
2178 if (file->IsCompressed)
2179 comp->ForceLocalState = TRUE;
2181 set_target_path( package, file );
2183 if ((comp->assembly && !comp->assembly->installed) ||
2184 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2186 comp->Cost += file->FileSize;
2187 continue;
2189 file_size = msi_get_disk_file_size( file->TargetPath );
2191 if (file->Version)
2193 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2195 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2197 comp->Cost += file->FileSize - file_size;
2199 msi_free( file_version );
2200 continue;
2202 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2204 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2206 comp->Cost += file->FileSize - file_size;
2208 msi_free( font_version );
2209 continue;
2212 if (file_size != file->FileSize)
2214 comp->Cost += file->FileSize - file_size;
2217 return ERROR_SUCCESS;
2220 WCHAR *msi_normalize_path( const WCHAR *in )
2222 const WCHAR *p = in;
2223 WCHAR *q, *ret;
2224 int n, len = strlenW( in ) + 2;
2226 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2228 len = 0;
2229 while (1)
2231 /* copy until the end of the string or a space */
2232 while (*p != ' ' && (*q = *p))
2234 p++, len++;
2235 /* reduce many backslashes to one */
2236 if (*p != '\\' || *q != '\\')
2237 q++;
2240 /* quit at the end of the string */
2241 if (!*p)
2242 break;
2244 /* count the number of spaces */
2245 n = 0;
2246 while (p[n] == ' ')
2247 n++;
2249 /* if it's leading or trailing space, skip it */
2250 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2251 p += n;
2252 else /* copy n spaces */
2253 while (n && (*q++ = *p++)) n--;
2255 while (q - ret > 0 && q[-1] == ' ') q--;
2256 if (q - ret > 0 && q[-1] != '\\')
2258 q[0] = '\\';
2259 q[1] = 0;
2261 return ret;
2264 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2266 FolderList *fl;
2267 MSIFOLDER *folder, *parent, *child;
2268 WCHAR *path, *normalized_path;
2270 TRACE("resolving %s\n", debugstr_w(name));
2272 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2274 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2276 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
2278 path = msi_dup_property( package->db, szRootDrive );
2281 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2283 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2285 parent = msi_get_loaded_folder( package, folder->Parent );
2286 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2288 else
2289 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2291 normalized_path = msi_normalize_path( path );
2292 msi_free( path );
2293 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2295 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2296 msi_free( normalized_path );
2297 return;
2299 msi_set_property( package->db, folder->Directory, normalized_path );
2300 msi_free( folder->ResolvedTarget );
2301 folder->ResolvedTarget = normalized_path;
2303 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2305 child = fl->folder;
2306 msi_resolve_target_folder( package, child->Directory, load_prop );
2308 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2311 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2313 static const WCHAR query[] = {
2314 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2315 '`','C','o','n','d','i','t','i','o','n','`',0};
2316 static const WCHAR szOutOfDiskSpace[] = {
2317 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2318 MSICOMPONENT *comp;
2319 MSIQUERY *view;
2320 LPWSTR level;
2321 UINT rc;
2323 TRACE("Building directory properties\n");
2324 msi_resolve_target_folder( package, szTargetDir, TRUE );
2326 TRACE("Evaluating component conditions\n");
2327 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2329 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2331 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2332 comp->Enabled = FALSE;
2334 else
2335 comp->Enabled = TRUE;
2338 /* read components states from the registry */
2339 ACTION_GetComponentInstallStates(package);
2340 ACTION_GetFeatureInstallStates(package);
2342 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2344 TRACE("Evaluating feature conditions\n");
2346 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2347 if (rc == ERROR_SUCCESS)
2349 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2350 msiobj_release( &view->hdr );
2351 if (rc != ERROR_SUCCESS)
2352 return rc;
2356 TRACE("Calculating file cost\n");
2357 calculate_file_cost( package );
2359 msi_set_property( package->db, szCostingComplete, szOne );
2360 /* set default run level if not set */
2361 level = msi_dup_property( package->db, szInstallLevel );
2362 if (!level)
2363 msi_set_property( package->db, szInstallLevel, szOne );
2364 msi_free(level);
2366 /* FIXME: check volume disk space */
2367 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2369 return MSI_SetFeatureStates(package);
2372 /* OK this value is "interpreted" and then formatted based on the
2373 first few characters */
2374 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2375 DWORD *size)
2377 LPSTR data = NULL;
2379 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2381 if (value[1]=='x')
2383 LPWSTR ptr;
2384 CHAR byte[5];
2385 LPWSTR deformated = NULL;
2386 int count;
2388 deformat_string(package, &value[2], &deformated);
2390 /* binary value type */
2391 ptr = deformated;
2392 *type = REG_BINARY;
2393 if (strlenW(ptr)%2)
2394 *size = (strlenW(ptr)/2)+1;
2395 else
2396 *size = strlenW(ptr)/2;
2398 data = msi_alloc(*size);
2400 byte[0] = '0';
2401 byte[1] = 'x';
2402 byte[4] = 0;
2403 count = 0;
2404 /* if uneven pad with a zero in front */
2405 if (strlenW(ptr)%2)
2407 byte[2]= '0';
2408 byte[3]= *ptr;
2409 ptr++;
2410 data[count] = (BYTE)strtol(byte,NULL,0);
2411 count ++;
2412 TRACE("Uneven byte count\n");
2414 while (*ptr)
2416 byte[2]= *ptr;
2417 ptr++;
2418 byte[3]= *ptr;
2419 ptr++;
2420 data[count] = (BYTE)strtol(byte,NULL,0);
2421 count ++;
2423 msi_free(deformated);
2425 TRACE("Data %i bytes(%i)\n",*size,count);
2427 else
2429 LPWSTR deformated;
2430 LPWSTR p;
2431 DWORD d = 0;
2432 deformat_string(package, &value[1], &deformated);
2434 *type=REG_DWORD;
2435 *size = sizeof(DWORD);
2436 data = msi_alloc(*size);
2437 p = deformated;
2438 if (*p == '-')
2439 p++;
2440 while (*p)
2442 if ( (*p < '0') || (*p > '9') )
2443 break;
2444 d *= 10;
2445 d += (*p - '0');
2446 p++;
2448 if (deformated[0] == '-')
2449 d = -d;
2450 *(LPDWORD)data = d;
2451 TRACE("DWORD %i\n",*(LPDWORD)data);
2453 msi_free(deformated);
2456 else
2458 static const WCHAR szMulti[] = {'[','~',']',0};
2459 LPCWSTR ptr;
2460 *type=REG_SZ;
2462 if (value[0]=='#')
2464 if (value[1]=='%')
2466 ptr = &value[2];
2467 *type=REG_EXPAND_SZ;
2469 else
2470 ptr = &value[1];
2472 else
2473 ptr=value;
2475 if (strstrW(value, szMulti))
2476 *type = REG_MULTI_SZ;
2478 /* remove initial delimiter */
2479 if (!strncmpW(value, szMulti, 3))
2480 ptr = value + 3;
2482 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2484 /* add double NULL terminator */
2485 if (*type == REG_MULTI_SZ)
2487 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2488 data = msi_realloc_zero(data, *size);
2491 return data;
2494 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2496 const WCHAR *ret;
2498 switch (root)
2500 case -1:
2501 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2503 *root_key = HKEY_LOCAL_MACHINE;
2504 ret = szHLM;
2506 else
2508 *root_key = HKEY_CURRENT_USER;
2509 ret = szHCU;
2511 break;
2512 case 0:
2513 *root_key = HKEY_CLASSES_ROOT;
2514 ret = szHCR;
2515 break;
2516 case 1:
2517 *root_key = HKEY_CURRENT_USER;
2518 ret = szHCU;
2519 break;
2520 case 2:
2521 *root_key = HKEY_LOCAL_MACHINE;
2522 ret = szHLM;
2523 break;
2524 case 3:
2525 *root_key = HKEY_USERS;
2526 ret = szHU;
2527 break;
2528 default:
2529 ERR("Unknown root %i\n", root);
2530 return NULL;
2533 return ret;
2536 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2538 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2539 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2541 if (is_64bit && package->platform == PLATFORM_INTEL &&
2542 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2544 UINT size;
2545 WCHAR *path_32node;
2547 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2548 if (!(path_32node = msi_alloc( size ))) return NULL;
2550 memcpy( path_32node, path, len * sizeof(WCHAR) );
2551 strcpyW( path_32node + len, szWow6432Node );
2552 strcatW( path_32node, szBackSlash );
2553 strcatW( path_32node, path + len );
2554 return path_32node;
2557 return strdupW( path );
2560 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2562 MSIPACKAGE *package = param;
2563 LPSTR value_data = NULL;
2564 HKEY root_key, hkey;
2565 DWORD type,size;
2566 LPWSTR deformated, uikey, keypath;
2567 LPCWSTR szRoot, component, name, key, value;
2568 MSICOMPONENT *comp;
2569 MSIRECORD * uirow;
2570 INT root;
2571 BOOL check_first = FALSE;
2572 UINT rc;
2574 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2576 component = MSI_RecordGetString(row, 6);
2577 comp = msi_get_loaded_component(package,component);
2578 if (!comp)
2579 return ERROR_SUCCESS;
2581 comp->Action = msi_get_component_action( package, comp );
2582 if (comp->Action != INSTALLSTATE_LOCAL)
2584 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2585 return ERROR_SUCCESS;
2588 name = MSI_RecordGetString(row, 4);
2589 if( MSI_RecordIsNull(row,5) && name )
2591 /* null values can have special meanings */
2592 if (name[0]=='-' && name[1] == 0)
2593 return ERROR_SUCCESS;
2594 else if ((name[0]=='+' && name[1] == 0) ||
2595 (name[0] == '*' && name[1] == 0))
2596 name = NULL;
2597 check_first = TRUE;
2600 root = MSI_RecordGetInteger(row,2);
2601 key = MSI_RecordGetString(row, 3);
2603 szRoot = get_root_key( package, root, &root_key );
2604 if (!szRoot)
2605 return ERROR_SUCCESS;
2607 deformat_string(package, key , &deformated);
2608 size = strlenW(deformated) + strlenW(szRoot) + 1;
2609 uikey = msi_alloc(size*sizeof(WCHAR));
2610 strcpyW(uikey,szRoot);
2611 strcatW(uikey,deformated);
2613 keypath = get_keypath( package, root_key, deformated );
2614 msi_free( deformated );
2615 if (RegCreateKeyW( root_key, keypath, &hkey ))
2617 ERR("Could not create key %s\n", debugstr_w(keypath));
2618 msi_free(uikey);
2619 msi_free(keypath);
2620 return ERROR_SUCCESS;
2623 value = MSI_RecordGetString(row,5);
2624 if (value)
2625 value_data = parse_value(package, value, &type, &size);
2626 else
2628 value_data = (LPSTR)strdupW(szEmpty);
2629 size = sizeof(szEmpty);
2630 type = REG_SZ;
2633 deformat_string(package, name, &deformated);
2635 if (!check_first)
2637 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2638 debugstr_w(uikey));
2639 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2641 else
2643 DWORD sz = 0;
2644 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2645 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2647 TRACE("value %s of %s checked already exists\n",
2648 debugstr_w(deformated), debugstr_w(uikey));
2650 else
2652 TRACE("Checked and setting value %s of %s\n",
2653 debugstr_w(deformated), debugstr_w(uikey));
2654 if (deformated || size)
2655 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2658 RegCloseKey(hkey);
2660 uirow = MSI_CreateRecord(3);
2661 MSI_RecordSetStringW(uirow,2,deformated);
2662 MSI_RecordSetStringW(uirow,1,uikey);
2663 if (type == REG_SZ || type == REG_EXPAND_SZ)
2664 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2665 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2666 msiobj_release( &uirow->hdr );
2668 msi_free(value_data);
2669 msi_free(deformated);
2670 msi_free(uikey);
2671 msi_free(keypath);
2673 return ERROR_SUCCESS;
2676 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2678 static const WCHAR query[] = {
2679 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2680 '`','R','e','g','i','s','t','r','y','`',0};
2681 MSIQUERY *view;
2682 UINT rc;
2684 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2685 if (rc != ERROR_SUCCESS)
2686 return ERROR_SUCCESS;
2688 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2689 msiobj_release(&view->hdr);
2690 return rc;
2693 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2695 LONG res;
2696 HKEY hkey;
2697 DWORD num_subkeys, num_values;
2699 if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2701 if ((res = RegDeleteValueW( hkey, value )))
2703 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2705 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2706 NULL, NULL, NULL, NULL );
2707 RegCloseKey( hkey );
2708 if (!res && !num_subkeys && !num_values)
2710 TRACE("removing empty key %s\n", debugstr_w(keypath));
2711 RegDeleteKeyW( root, keypath );
2713 return;
2715 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2718 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2720 LONG res = RegDeleteTreeW( root, keypath );
2721 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2724 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2726 MSIPACKAGE *package = param;
2727 LPCWSTR component, name, key_str, root_key_str;
2728 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2729 MSICOMPONENT *comp;
2730 MSIRECORD *uirow;
2731 BOOL delete_key = FALSE;
2732 HKEY hkey_root;
2733 UINT size;
2734 INT root;
2736 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2738 component = MSI_RecordGetString( row, 6 );
2739 comp = msi_get_loaded_component( package, component );
2740 if (!comp)
2741 return ERROR_SUCCESS;
2743 comp->Action = msi_get_component_action( package, comp );
2744 if (comp->Action != INSTALLSTATE_ABSENT)
2746 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2747 return ERROR_SUCCESS;
2750 name = MSI_RecordGetString( row, 4 );
2751 if (MSI_RecordIsNull( row, 5 ) && name )
2753 if (name[0] == '+' && !name[1])
2754 return ERROR_SUCCESS;
2755 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2757 delete_key = TRUE;
2758 name = NULL;
2762 root = MSI_RecordGetInteger( row, 2 );
2763 key_str = MSI_RecordGetString( row, 3 );
2765 root_key_str = get_root_key( package, root, &hkey_root );
2766 if (!root_key_str)
2767 return ERROR_SUCCESS;
2769 deformat_string( package, key_str, &deformated_key );
2770 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2771 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2772 strcpyW( ui_key_str, root_key_str );
2773 strcatW( ui_key_str, deformated_key );
2775 deformat_string( package, name, &deformated_name );
2777 keypath = get_keypath( package, hkey_root, deformated_key );
2778 msi_free( deformated_key );
2779 if (delete_key) delete_reg_key( hkey_root, keypath );
2780 else delete_reg_value( hkey_root, keypath, deformated_name );
2781 msi_free( keypath );
2783 uirow = MSI_CreateRecord( 2 );
2784 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2785 MSI_RecordSetStringW( uirow, 2, deformated_name );
2786 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2787 msiobj_release( &uirow->hdr );
2789 msi_free( ui_key_str );
2790 msi_free( deformated_name );
2791 return ERROR_SUCCESS;
2794 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2796 MSIPACKAGE *package = param;
2797 LPCWSTR component, name, key_str, root_key_str;
2798 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2799 MSICOMPONENT *comp;
2800 MSIRECORD *uirow;
2801 BOOL delete_key = FALSE;
2802 HKEY hkey_root;
2803 UINT size;
2804 INT root;
2806 component = MSI_RecordGetString( row, 5 );
2807 comp = msi_get_loaded_component( package, component );
2808 if (!comp)
2809 return ERROR_SUCCESS;
2811 comp->Action = msi_get_component_action( package, comp );
2812 if (comp->Action != INSTALLSTATE_LOCAL)
2814 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2815 return ERROR_SUCCESS;
2818 if ((name = MSI_RecordGetString( row, 4 )))
2820 if (name[0] == '-' && !name[1])
2822 delete_key = TRUE;
2823 name = NULL;
2827 root = MSI_RecordGetInteger( row, 2 );
2828 key_str = MSI_RecordGetString( row, 3 );
2830 root_key_str = get_root_key( package, root, &hkey_root );
2831 if (!root_key_str)
2832 return ERROR_SUCCESS;
2834 deformat_string( package, key_str, &deformated_key );
2835 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2836 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2837 strcpyW( ui_key_str, root_key_str );
2838 strcatW( ui_key_str, deformated_key );
2840 deformat_string( package, name, &deformated_name );
2842 keypath = get_keypath( package, hkey_root, deformated_key );
2843 msi_free( deformated_key );
2844 if (delete_key) delete_reg_key( hkey_root, keypath );
2845 else delete_reg_value( hkey_root, keypath, deformated_name );
2846 msi_free( keypath );
2848 uirow = MSI_CreateRecord( 2 );
2849 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2850 MSI_RecordSetStringW( uirow, 2, deformated_name );
2851 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2852 msiobj_release( &uirow->hdr );
2854 msi_free( ui_key_str );
2855 msi_free( deformated_name );
2856 return ERROR_SUCCESS;
2859 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2861 static const WCHAR registry_query[] = {
2862 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2863 '`','R','e','g','i','s','t','r','y','`',0};
2864 static const WCHAR remove_registry_query[] = {
2865 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2866 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2867 MSIQUERY *view;
2868 UINT rc;
2870 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2871 if (rc == ERROR_SUCCESS)
2873 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2874 msiobj_release( &view->hdr );
2875 if (rc != ERROR_SUCCESS)
2876 return rc;
2878 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2879 if (rc == ERROR_SUCCESS)
2881 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2882 msiobj_release( &view->hdr );
2883 if (rc != ERROR_SUCCESS)
2884 return rc;
2886 return ERROR_SUCCESS;
2889 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2891 package->script->CurrentlyScripting = TRUE;
2893 return ERROR_SUCCESS;
2897 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2899 static const WCHAR query[]= {
2900 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2901 '`','R','e','g','i','s','t','r','y','`',0};
2902 MSICOMPONENT *comp;
2903 DWORD total = 0, count = 0;
2904 MSIQUERY *view;
2905 MSIFEATURE *feature;
2906 MSIFILE *file;
2907 UINT rc;
2909 TRACE("InstallValidate\n");
2911 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2912 if (rc == ERROR_SUCCESS)
2914 rc = MSI_IterateRecords( view, &count, NULL, package );
2915 msiobj_release( &view->hdr );
2916 if (rc != ERROR_SUCCESS)
2917 return rc;
2918 total += count * REG_PROGRESS_VALUE;
2920 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2921 total += COMPONENT_PROGRESS_VALUE;
2923 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2924 total += file->FileSize;
2926 msi_ui_progress( package, 0, total, 0, 0 );
2928 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2930 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2931 debugstr_w(feature->Feature), feature->Installed,
2932 feature->ActionRequest, feature->Action);
2934 return ERROR_SUCCESS;
2937 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2939 MSIPACKAGE* package = param;
2940 LPCWSTR cond = NULL;
2941 LPCWSTR message = NULL;
2942 UINT r;
2944 static const WCHAR title[]=
2945 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2947 cond = MSI_RecordGetString(row,1);
2949 r = MSI_EvaluateConditionW(package,cond);
2950 if (r == MSICONDITION_FALSE)
2952 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2954 LPWSTR deformated;
2955 message = MSI_RecordGetString(row,2);
2956 deformat_string(package,message,&deformated);
2957 MessageBoxW(NULL,deformated,title,MB_OK);
2958 msi_free(deformated);
2961 return ERROR_INSTALL_FAILURE;
2964 return ERROR_SUCCESS;
2967 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2969 static const WCHAR query[] = {
2970 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2971 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2972 MSIQUERY *view;
2973 UINT rc;
2975 TRACE("Checking launch conditions\n");
2977 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2978 if (rc != ERROR_SUCCESS)
2979 return ERROR_SUCCESS;
2981 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2982 msiobj_release(&view->hdr);
2983 return rc;
2986 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2989 if (!cmp->KeyPath)
2990 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2992 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2994 static const WCHAR query[] = {
2995 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2996 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2997 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2998 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
2999 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3000 MSIRECORD *row;
3001 UINT root, len;
3002 LPWSTR deformated, buffer, deformated_name;
3003 LPCWSTR key, name;
3005 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3006 if (!row)
3007 return NULL;
3009 root = MSI_RecordGetInteger(row,2);
3010 key = MSI_RecordGetString(row, 3);
3011 name = MSI_RecordGetString(row, 4);
3012 deformat_string(package, key , &deformated);
3013 deformat_string(package, name, &deformated_name);
3015 len = strlenW(deformated) + 6;
3016 if (deformated_name)
3017 len+=strlenW(deformated_name);
3019 buffer = msi_alloc( len *sizeof(WCHAR));
3021 if (deformated_name)
3022 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3023 else
3024 sprintfW(buffer,fmt,root,deformated);
3026 msi_free(deformated);
3027 msi_free(deformated_name);
3028 msiobj_release(&row->hdr);
3030 return buffer;
3032 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3034 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3035 return NULL;
3037 else
3039 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3041 if (file)
3042 return strdupW( file->TargetPath );
3044 return NULL;
3047 static HKEY openSharedDLLsKey(void)
3049 HKEY hkey=0;
3050 static const WCHAR path[] =
3051 {'S','o','f','t','w','a','r','e','\\',
3052 'M','i','c','r','o','s','o','f','t','\\',
3053 'W','i','n','d','o','w','s','\\',
3054 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3055 'S','h','a','r','e','d','D','L','L','s',0};
3057 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3058 return hkey;
3061 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3063 HKEY hkey;
3064 DWORD count=0;
3065 DWORD type;
3066 DWORD sz = sizeof(count);
3067 DWORD rc;
3069 hkey = openSharedDLLsKey();
3070 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3071 if (rc != ERROR_SUCCESS)
3072 count = 0;
3073 RegCloseKey(hkey);
3074 return count;
3077 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3079 HKEY hkey;
3081 hkey = openSharedDLLsKey();
3082 if (count > 0)
3083 msi_reg_set_val_dword( hkey, path, count );
3084 else
3085 RegDeleteValueW(hkey,path);
3086 RegCloseKey(hkey);
3087 return count;
3090 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3092 MSIFEATURE *feature;
3093 INT count = 0;
3094 BOOL write = FALSE;
3096 /* only refcount DLLs */
3097 if (comp->KeyPath == NULL ||
3098 comp->assembly ||
3099 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3100 comp->Attributes & msidbComponentAttributesODBCDataSource)
3101 write = FALSE;
3102 else
3104 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3105 write = (count > 0);
3107 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3108 write = TRUE;
3111 /* increment counts */
3112 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3114 ComponentList *cl;
3116 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3117 continue;
3119 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3121 if ( cl->component == comp )
3122 count++;
3126 /* decrement counts */
3127 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3129 ComponentList *cl;
3131 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3132 continue;
3134 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3136 if ( cl->component == comp )
3137 count--;
3141 /* ref count all the files in the component */
3142 if (write)
3144 MSIFILE *file;
3146 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3148 if (file->Component == comp)
3149 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3153 /* add a count for permanent */
3154 if (comp->Attributes & msidbComponentAttributesPermanent)
3155 count ++;
3157 comp->RefCount = count;
3159 if (write)
3160 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3163 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3165 if (comp->assembly)
3167 const WCHAR prefixW[] = {'<','\\',0};
3168 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3169 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3171 if (keypath)
3173 strcpyW( keypath, prefixW );
3174 strcatW( keypath, comp->assembly->display_name );
3176 return keypath;
3178 return resolve_keypath( package, comp );
3181 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3183 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3184 UINT rc;
3185 MSICOMPONENT *comp;
3186 HKEY hkey;
3188 TRACE("\n");
3190 squash_guid(package->ProductCode,squished_pc);
3191 msi_set_sourcedir_props(package, FALSE);
3193 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3195 MSIRECORD *uirow;
3196 INSTALLSTATE action;
3198 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3199 if (!comp->ComponentId)
3200 continue;
3202 squash_guid( comp->ComponentId, squished_cc );
3203 msi_free( comp->FullKeypath );
3204 comp->FullKeypath = build_full_keypath( package, comp );
3206 ACTION_RefCountComponent( package, comp );
3208 if (package->need_rollback) action = comp->Installed;
3209 else action = comp->ActionRequest;
3211 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3212 debugstr_w(comp->Component), debugstr_w(squished_cc),
3213 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3215 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3217 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3218 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3219 else
3220 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3222 if (rc != ERROR_SUCCESS)
3223 continue;
3225 if (comp->Attributes & msidbComponentAttributesPermanent)
3227 static const WCHAR szPermKey[] =
3228 { '0','0','0','0','0','0','0','0','0','0','0','0',
3229 '0','0','0','0','0','0','0','0','0','0','0','0',
3230 '0','0','0','0','0','0','0','0',0 };
3232 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3234 if (action == INSTALLSTATE_LOCAL)
3235 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3236 else
3238 MSIFILE *file;
3239 MSIRECORD *row;
3240 LPWSTR ptr, ptr2;
3241 WCHAR source[MAX_PATH];
3242 WCHAR base[MAX_PATH];
3243 LPWSTR sourcepath;
3245 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3246 static const WCHAR query[] = {
3247 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3248 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3249 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3250 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3251 '`','D','i','s','k','I','d','`',0};
3253 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3254 continue;
3256 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3257 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3258 ptr2 = strrchrW(source, '\\') + 1;
3259 msiobj_release(&row->hdr);
3261 lstrcpyW(base, package->PackagePath);
3262 ptr = strrchrW(base, '\\');
3263 *(ptr + 1) = '\0';
3265 sourcepath = msi_resolve_file_source(package, file);
3266 ptr = sourcepath + lstrlenW(base);
3267 lstrcpyW(ptr2, ptr);
3268 msi_free(sourcepath);
3270 msi_reg_set_val_str(hkey, squished_pc, source);
3272 RegCloseKey(hkey);
3274 else if (action == INSTALLSTATE_ABSENT)
3276 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3277 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3278 else
3279 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3282 /* UI stuff */
3283 uirow = MSI_CreateRecord(3);
3284 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3285 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3286 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3287 msi_ui_actiondata( package, szProcessComponents, uirow );
3288 msiobj_release( &uirow->hdr );
3290 return ERROR_SUCCESS;
3293 typedef struct {
3294 CLSID clsid;
3295 LPWSTR source;
3297 LPWSTR path;
3298 ITypeLib *ptLib;
3299 } typelib_struct;
3301 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3302 LPWSTR lpszName, LONG_PTR lParam)
3304 TLIBATTR *attr;
3305 typelib_struct *tl_struct = (typelib_struct*) lParam;
3306 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3307 int sz;
3308 HRESULT res;
3310 if (!IS_INTRESOURCE(lpszName))
3312 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3313 return TRUE;
3316 sz = strlenW(tl_struct->source)+4;
3317 sz *= sizeof(WCHAR);
3319 if ((INT_PTR)lpszName == 1)
3320 tl_struct->path = strdupW(tl_struct->source);
3321 else
3323 tl_struct->path = msi_alloc(sz);
3324 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3327 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3328 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3329 if (FAILED(res))
3331 msi_free(tl_struct->path);
3332 tl_struct->path = NULL;
3334 return TRUE;
3337 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3338 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3340 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3341 return FALSE;
3344 msi_free(tl_struct->path);
3345 tl_struct->path = NULL;
3347 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3348 ITypeLib_Release(tl_struct->ptLib);
3350 return TRUE;
3353 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3355 MSIPACKAGE* package = param;
3356 LPCWSTR component;
3357 MSICOMPONENT *comp;
3358 MSIFILE *file;
3359 typelib_struct tl_struct;
3360 ITypeLib *tlib;
3361 HMODULE module;
3362 HRESULT hr;
3364 component = MSI_RecordGetString(row,3);
3365 comp = msi_get_loaded_component(package,component);
3366 if (!comp)
3367 return ERROR_SUCCESS;
3369 comp->Action = msi_get_component_action( package, comp );
3370 if (comp->Action != INSTALLSTATE_LOCAL)
3372 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3373 return ERROR_SUCCESS;
3376 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3378 TRACE("component has no key path\n");
3379 return ERROR_SUCCESS;
3381 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3383 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3384 if (module)
3386 LPCWSTR guid;
3387 guid = MSI_RecordGetString(row,1);
3388 CLSIDFromString( guid, &tl_struct.clsid);
3389 tl_struct.source = strdupW( file->TargetPath );
3390 tl_struct.path = NULL;
3392 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3393 (LONG_PTR)&tl_struct);
3395 if (tl_struct.path)
3397 LPCWSTR helpid, help_path = NULL;
3398 HRESULT res;
3400 helpid = MSI_RecordGetString(row,6);
3402 if (helpid) help_path = msi_get_target_folder( package, helpid );
3403 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3405 if (FAILED(res))
3406 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3407 else
3408 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3410 ITypeLib_Release(tl_struct.ptLib);
3411 msi_free(tl_struct.path);
3413 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3415 FreeLibrary(module);
3416 msi_free(tl_struct.source);
3418 else
3420 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3421 if (FAILED(hr))
3423 ERR("Failed to load type library: %08x\n", hr);
3424 return ERROR_INSTALL_FAILURE;
3427 ITypeLib_Release(tlib);
3430 return ERROR_SUCCESS;
3433 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3435 static const WCHAR query[] = {
3436 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3437 '`','T','y','p','e','L','i','b','`',0};
3438 MSIQUERY *view;
3439 UINT rc;
3441 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3442 if (rc != ERROR_SUCCESS)
3443 return ERROR_SUCCESS;
3445 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3446 msiobj_release(&view->hdr);
3447 return rc;
3450 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3452 MSIPACKAGE *package = param;
3453 LPCWSTR component, guid;
3454 MSICOMPONENT *comp;
3455 GUID libid;
3456 UINT version;
3457 LCID language;
3458 SYSKIND syskind;
3459 HRESULT hr;
3461 component = MSI_RecordGetString( row, 3 );
3462 comp = msi_get_loaded_component( package, component );
3463 if (!comp)
3464 return ERROR_SUCCESS;
3466 comp->Action = msi_get_component_action( package, comp );
3467 if (comp->Action != INSTALLSTATE_ABSENT)
3469 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3470 return ERROR_SUCCESS;
3472 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3474 guid = MSI_RecordGetString( row, 1 );
3475 CLSIDFromString( guid, &libid );
3476 version = MSI_RecordGetInteger( row, 4 );
3477 language = MSI_RecordGetInteger( row, 2 );
3479 #ifdef _WIN64
3480 syskind = SYS_WIN64;
3481 #else
3482 syskind = SYS_WIN32;
3483 #endif
3485 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3486 if (FAILED(hr))
3488 WARN("Failed to unregister typelib: %08x\n", hr);
3491 return ERROR_SUCCESS;
3494 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3496 static const WCHAR query[] = {
3497 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3498 '`','T','y','p','e','L','i','b','`',0};
3499 MSIQUERY *view;
3500 UINT rc;
3502 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3503 if (rc != ERROR_SUCCESS)
3504 return ERROR_SUCCESS;
3506 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3507 msiobj_release( &view->hdr );
3508 return rc;
3511 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3513 static const WCHAR szlnk[] = {'.','l','n','k',0};
3514 LPCWSTR directory, extension, link_folder;
3515 LPWSTR link_file, filename;
3517 directory = MSI_RecordGetString( row, 2 );
3518 link_folder = msi_get_target_folder( package, directory );
3519 if (!link_folder)
3521 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3522 return NULL;
3524 /* may be needed because of a bug somewhere else */
3525 msi_create_full_path( link_folder );
3527 filename = msi_dup_record_field( row, 3 );
3528 msi_reduce_to_long_filename( filename );
3530 extension = strchrW( filename, '.' );
3531 if (!extension || strcmpiW( extension, szlnk ))
3533 int len = strlenW( filename );
3534 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3535 memcpy( filename + len, szlnk, sizeof(szlnk) );
3537 link_file = msi_build_directory_name( 2, link_folder, filename );
3538 msi_free( filename );
3540 return link_file;
3543 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3545 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3546 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3547 WCHAR *folder, *dest, *path;
3549 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3550 folder = msi_dup_property( package->db, szWindowsFolder );
3551 else
3553 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3554 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3555 msi_free( appdata );
3557 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3558 msi_create_full_path( dest );
3559 path = msi_build_directory_name( 2, dest, icon_name );
3560 msi_free( folder );
3561 msi_free( dest );
3562 return path;
3565 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3567 MSIPACKAGE *package = param;
3568 LPWSTR link_file, deformated, path;
3569 LPCWSTR component, target;
3570 MSICOMPONENT *comp;
3571 IShellLinkW *sl = NULL;
3572 IPersistFile *pf = NULL;
3573 HRESULT res;
3575 component = MSI_RecordGetString(row, 4);
3576 comp = msi_get_loaded_component(package, component);
3577 if (!comp)
3578 return ERROR_SUCCESS;
3580 comp->Action = msi_get_component_action( package, comp );
3581 if (comp->Action != INSTALLSTATE_LOCAL)
3583 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3584 return ERROR_SUCCESS;
3586 msi_ui_actiondata( package, szCreateShortcuts, row );
3588 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3589 &IID_IShellLinkW, (LPVOID *) &sl );
3591 if (FAILED( res ))
3593 ERR("CLSID_ShellLink not available\n");
3594 goto err;
3597 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3598 if (FAILED( res ))
3600 ERR("QueryInterface(IID_IPersistFile) failed\n");
3601 goto err;
3604 target = MSI_RecordGetString(row, 5);
3605 if (strchrW(target, '['))
3607 deformat_string( package, target, &path );
3608 TRACE("target path is %s\n", debugstr_w(path));
3609 IShellLinkW_SetPath( sl, path );
3610 msi_free( path );
3612 else
3614 FIXME("poorly handled shortcut format, advertised shortcut\n");
3615 IShellLinkW_SetPath(sl,comp->FullKeypath);
3618 if (!MSI_RecordIsNull(row,6))
3620 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3621 deformat_string(package, arguments, &deformated);
3622 IShellLinkW_SetArguments(sl,deformated);
3623 msi_free(deformated);
3626 if (!MSI_RecordIsNull(row,7))
3628 LPCWSTR description = MSI_RecordGetString(row, 7);
3629 IShellLinkW_SetDescription(sl, description);
3632 if (!MSI_RecordIsNull(row,8))
3633 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3635 if (!MSI_RecordIsNull(row,9))
3637 INT index;
3638 LPCWSTR icon = MSI_RecordGetString(row, 9);
3640 path = msi_build_icon_path(package, icon);
3641 index = MSI_RecordGetInteger(row,10);
3643 /* no value means 0 */
3644 if (index == MSI_NULL_INTEGER)
3645 index = 0;
3647 IShellLinkW_SetIconLocation(sl, path, index);
3648 msi_free(path);
3651 if (!MSI_RecordIsNull(row,11))
3652 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3654 if (!MSI_RecordIsNull(row,12))
3656 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3657 full_path = msi_get_target_folder( package, wkdir );
3658 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3660 link_file = get_link_file(package, row);
3662 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3663 IPersistFile_Save(pf, link_file, FALSE);
3664 msi_free(link_file);
3666 err:
3667 if (pf)
3668 IPersistFile_Release( pf );
3669 if (sl)
3670 IShellLinkW_Release( sl );
3672 return ERROR_SUCCESS;
3675 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3677 static const WCHAR query[] = {
3678 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3679 '`','S','h','o','r','t','c','u','t','`',0};
3680 MSIQUERY *view;
3681 HRESULT res;
3682 UINT rc;
3684 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3685 if (rc != ERROR_SUCCESS)
3686 return ERROR_SUCCESS;
3688 res = CoInitialize( NULL );
3690 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3691 msiobj_release(&view->hdr);
3693 if (SUCCEEDED(res)) CoUninitialize();
3694 return rc;
3697 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3699 MSIPACKAGE *package = param;
3700 LPWSTR link_file;
3701 LPCWSTR component;
3702 MSICOMPONENT *comp;
3704 component = MSI_RecordGetString( row, 4 );
3705 comp = msi_get_loaded_component( package, component );
3706 if (!comp)
3707 return ERROR_SUCCESS;
3709 comp->Action = msi_get_component_action( package, comp );
3710 if (comp->Action != INSTALLSTATE_ABSENT)
3712 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3713 return ERROR_SUCCESS;
3715 msi_ui_actiondata( package, szRemoveShortcuts, row );
3717 link_file = get_link_file( package, row );
3719 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3720 if (!DeleteFileW( link_file ))
3722 WARN("Failed to remove shortcut file %u\n", GetLastError());
3724 msi_free( link_file );
3726 return ERROR_SUCCESS;
3729 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3731 static const WCHAR query[] = {
3732 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3733 '`','S','h','o','r','t','c','u','t','`',0};
3734 MSIQUERY *view;
3735 UINT rc;
3737 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3738 if (rc != ERROR_SUCCESS)
3739 return ERROR_SUCCESS;
3741 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3742 msiobj_release( &view->hdr );
3743 return rc;
3746 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3748 MSIPACKAGE* package = param;
3749 HANDLE the_file;
3750 LPWSTR FilePath;
3751 LPCWSTR FileName;
3752 CHAR buffer[1024];
3753 DWORD sz;
3754 UINT rc;
3756 FileName = MSI_RecordGetString(row,1);
3757 if (!FileName)
3759 ERR("Unable to get FileName\n");
3760 return ERROR_SUCCESS;
3763 FilePath = msi_build_icon_path(package, FileName);
3765 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3767 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3768 FILE_ATTRIBUTE_NORMAL, NULL);
3770 if (the_file == INVALID_HANDLE_VALUE)
3772 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3773 msi_free(FilePath);
3774 return ERROR_SUCCESS;
3779 DWORD write;
3780 sz = 1024;
3781 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3782 if (rc != ERROR_SUCCESS)
3784 ERR("Failed to get stream\n");
3785 CloseHandle(the_file);
3786 DeleteFileW(FilePath);
3787 break;
3789 WriteFile(the_file,buffer,sz,&write,NULL);
3790 } while (sz == 1024);
3792 msi_free(FilePath);
3793 CloseHandle(the_file);
3795 return ERROR_SUCCESS;
3798 static UINT msi_publish_icons(MSIPACKAGE *package)
3800 static const WCHAR query[]= {
3801 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3802 '`','I','c','o','n','`',0};
3803 MSIQUERY *view;
3804 UINT r;
3806 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3807 if (r == ERROR_SUCCESS)
3809 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3810 msiobj_release(&view->hdr);
3811 if (r != ERROR_SUCCESS)
3812 return r;
3814 return ERROR_SUCCESS;
3817 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3819 UINT r;
3820 HKEY source;
3821 LPWSTR buffer;
3822 MSIMEDIADISK *disk;
3823 MSISOURCELISTINFO *info;
3825 r = RegCreateKeyW(hkey, szSourceList, &source);
3826 if (r != ERROR_SUCCESS)
3827 return r;
3829 RegCloseKey(source);
3831 buffer = strrchrW(package->PackagePath, '\\') + 1;
3832 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3833 package->Context, MSICODE_PRODUCT,
3834 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3835 if (r != ERROR_SUCCESS)
3836 return r;
3838 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3839 package->Context, MSICODE_PRODUCT,
3840 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3841 if (r != ERROR_SUCCESS)
3842 return r;
3844 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3845 package->Context, MSICODE_PRODUCT,
3846 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3847 if (r != ERROR_SUCCESS)
3848 return r;
3850 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3852 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3853 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3854 info->options, info->value);
3855 else
3856 MsiSourceListSetInfoW(package->ProductCode, NULL,
3857 info->context, info->options,
3858 info->property, info->value);
3861 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3863 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3864 disk->context, disk->options,
3865 disk->disk_id, disk->volume_label, disk->disk_prompt);
3868 return ERROR_SUCCESS;
3871 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3873 MSIHANDLE hdb, suminfo;
3874 WCHAR guids[MAX_PATH];
3875 WCHAR packcode[SQUISH_GUID_SIZE];
3876 LPWSTR buffer;
3877 LPWSTR ptr;
3878 DWORD langid;
3879 DWORD size;
3880 UINT r;
3882 static const WCHAR szARPProductIcon[] =
3883 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3884 static const WCHAR szAssignment[] =
3885 {'A','s','s','i','g','n','m','e','n','t',0};
3886 static const WCHAR szAdvertiseFlags[] =
3887 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3888 static const WCHAR szClients[] =
3889 {'C','l','i','e','n','t','s',0};
3890 static const WCHAR szColon[] = {':',0};
3892 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3893 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3894 msi_free(buffer);
3896 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3897 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3899 /* FIXME */
3900 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3902 buffer = msi_dup_property(package->db, szARPProductIcon);
3903 if (buffer)
3905 LPWSTR path = msi_build_icon_path(package, buffer);
3906 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3907 msi_free(path);
3908 msi_free(buffer);
3911 buffer = msi_dup_property(package->db, szProductVersion);
3912 if (buffer)
3914 DWORD verdword = msi_version_str_to_dword(buffer);
3915 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3916 msi_free(buffer);
3919 msi_reg_set_val_dword(hkey, szAssignment, 0);
3920 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3921 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3922 msi_reg_set_val_str(hkey, szClients, szColon);
3924 hdb = alloc_msihandle(&package->db->hdr);
3925 if (!hdb)
3926 return ERROR_NOT_ENOUGH_MEMORY;
3928 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3929 MsiCloseHandle(hdb);
3930 if (r != ERROR_SUCCESS)
3931 goto done;
3933 size = MAX_PATH;
3934 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3935 NULL, guids, &size);
3936 if (r != ERROR_SUCCESS)
3937 goto done;
3939 ptr = strchrW(guids, ';');
3940 if (ptr) *ptr = 0;
3941 squash_guid(guids, packcode);
3942 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3944 done:
3945 MsiCloseHandle(suminfo);
3946 return ERROR_SUCCESS;
3949 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3951 UINT r;
3952 HKEY hkey;
3953 LPWSTR upgrade;
3954 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3956 upgrade = msi_dup_property(package->db, szUpgradeCode);
3957 if (!upgrade)
3958 return ERROR_SUCCESS;
3960 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3961 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3962 else
3963 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3965 if (r != ERROR_SUCCESS)
3967 WARN("failed to open upgrade code key\n");
3968 msi_free(upgrade);
3969 return ERROR_SUCCESS;
3971 squash_guid(package->ProductCode, squashed_pc);
3972 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3973 RegCloseKey(hkey);
3974 msi_free(upgrade);
3975 return ERROR_SUCCESS;
3978 static BOOL msi_check_publish(MSIPACKAGE *package)
3980 MSIFEATURE *feature;
3982 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3984 feature->Action = msi_get_feature_action( package, feature );
3985 if (feature->Action == INSTALLSTATE_LOCAL)
3986 return TRUE;
3989 return FALSE;
3992 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3994 MSIFEATURE *feature;
3996 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3998 feature->Action = msi_get_feature_action( package, feature );
3999 if (feature->Action != INSTALLSTATE_ABSENT)
4000 return FALSE;
4003 return TRUE;
4006 static UINT msi_publish_patches( MSIPACKAGE *package )
4008 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4009 WCHAR patch_squashed[GUID_SIZE];
4010 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4011 LONG res;
4012 MSIPATCHINFO *patch;
4013 UINT r;
4014 WCHAR *p, *all_patches = NULL;
4015 DWORD len = 0;
4017 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4018 if (r != ERROR_SUCCESS)
4019 return ERROR_FUNCTION_FAILED;
4021 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4022 if (res != ERROR_SUCCESS)
4024 r = ERROR_FUNCTION_FAILED;
4025 goto done;
4028 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4029 if (r != ERROR_SUCCESS)
4030 goto done;
4032 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4034 squash_guid( patch->patchcode, patch_squashed );
4035 len += strlenW( patch_squashed ) + 1;
4038 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4039 if (!all_patches)
4040 goto done;
4042 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4044 HKEY patch_key;
4046 squash_guid( patch->patchcode, p );
4047 p += strlenW( p ) + 1;
4049 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4050 (const BYTE *)patch->transforms,
4051 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4052 if (res != ERROR_SUCCESS)
4053 goto done;
4055 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4056 if (r != ERROR_SUCCESS)
4057 goto done;
4059 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4060 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4061 RegCloseKey( patch_key );
4062 if (res != ERROR_SUCCESS)
4063 goto done;
4065 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4067 res = GetLastError();
4068 ERR("Unable to copy patch package %d\n", res);
4069 goto done;
4071 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4072 if (res != ERROR_SUCCESS)
4073 goto done;
4075 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4076 RegCloseKey( patch_key );
4077 if (res != ERROR_SUCCESS)
4078 goto done;
4081 all_patches[len] = 0;
4082 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4083 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4084 if (res != ERROR_SUCCESS)
4085 goto done;
4087 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4088 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4089 if (res != ERROR_SUCCESS)
4090 r = ERROR_FUNCTION_FAILED;
4092 done:
4093 RegCloseKey( product_patches_key );
4094 RegCloseKey( patches_key );
4095 RegCloseKey( product_key );
4096 msi_free( all_patches );
4097 return r;
4100 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4102 UINT rc;
4103 HKEY hukey = NULL, hudkey = NULL;
4104 MSIRECORD *uirow;
4106 if (!list_empty(&package->patches))
4108 rc = msi_publish_patches(package);
4109 if (rc != ERROR_SUCCESS)
4110 goto end;
4113 /* FIXME: also need to publish if the product is in advertise mode */
4114 if (!msi_check_publish(package))
4115 return ERROR_SUCCESS;
4117 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4118 &hukey, TRUE);
4119 if (rc != ERROR_SUCCESS)
4120 goto end;
4122 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4123 NULL, &hudkey, TRUE);
4124 if (rc != ERROR_SUCCESS)
4125 goto end;
4127 rc = msi_publish_upgrade_code(package);
4128 if (rc != ERROR_SUCCESS)
4129 goto end;
4131 rc = msi_publish_product_properties(package, hukey);
4132 if (rc != ERROR_SUCCESS)
4133 goto end;
4135 rc = msi_publish_sourcelist(package, hukey);
4136 if (rc != ERROR_SUCCESS)
4137 goto end;
4139 rc = msi_publish_icons(package);
4141 end:
4142 uirow = MSI_CreateRecord( 1 );
4143 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4144 msi_ui_actiondata( package, szPublishProduct, uirow );
4145 msiobj_release( &uirow->hdr );
4147 RegCloseKey(hukey);
4148 RegCloseKey(hudkey);
4149 return rc;
4152 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4154 WCHAR *filename, *ptr, *folder, *ret;
4155 const WCHAR *dirprop;
4157 filename = msi_dup_record_field( row, 2 );
4158 if (filename && (ptr = strchrW( filename, '|' )))
4159 ptr++;
4160 else
4161 ptr = filename;
4163 dirprop = MSI_RecordGetString( row, 3 );
4164 if (dirprop)
4166 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4167 if (!folder) folder = msi_dup_property( package->db, dirprop );
4169 else
4170 folder = msi_dup_property( package->db, szWindowsFolder );
4172 if (!folder)
4174 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4175 msi_free( filename );
4176 return NULL;
4179 ret = msi_build_directory_name( 2, folder, ptr );
4181 msi_free( filename );
4182 msi_free( folder );
4183 return ret;
4186 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4188 MSIPACKAGE *package = param;
4189 LPCWSTR component, section, key, value, identifier;
4190 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4191 MSIRECORD * uirow;
4192 INT action;
4193 MSICOMPONENT *comp;
4195 component = MSI_RecordGetString(row, 8);
4196 comp = msi_get_loaded_component(package,component);
4197 if (!comp)
4198 return ERROR_SUCCESS;
4200 comp->Action = msi_get_component_action( package, comp );
4201 if (comp->Action != INSTALLSTATE_LOCAL)
4203 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4204 return ERROR_SUCCESS;
4207 identifier = MSI_RecordGetString(row,1);
4208 section = MSI_RecordGetString(row,4);
4209 key = MSI_RecordGetString(row,5);
4210 value = MSI_RecordGetString(row,6);
4211 action = MSI_RecordGetInteger(row,7);
4213 deformat_string(package,section,&deformated_section);
4214 deformat_string(package,key,&deformated_key);
4215 deformat_string(package,value,&deformated_value);
4217 fullname = get_ini_file_name(package, row);
4219 if (action == 0)
4221 TRACE("Adding value %s to section %s in %s\n",
4222 debugstr_w(deformated_key), debugstr_w(deformated_section),
4223 debugstr_w(fullname));
4224 WritePrivateProfileStringW(deformated_section, deformated_key,
4225 deformated_value, fullname);
4227 else if (action == 1)
4229 WCHAR returned[10];
4230 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4231 returned, 10, fullname);
4232 if (returned[0] == 0)
4234 TRACE("Adding value %s to section %s in %s\n",
4235 debugstr_w(deformated_key), debugstr_w(deformated_section),
4236 debugstr_w(fullname));
4238 WritePrivateProfileStringW(deformated_section, deformated_key,
4239 deformated_value, fullname);
4242 else if (action == 3)
4243 FIXME("Append to existing section not yet implemented\n");
4245 uirow = MSI_CreateRecord(4);
4246 MSI_RecordSetStringW(uirow,1,identifier);
4247 MSI_RecordSetStringW(uirow,2,deformated_section);
4248 MSI_RecordSetStringW(uirow,3,deformated_key);
4249 MSI_RecordSetStringW(uirow,4,deformated_value);
4250 msi_ui_actiondata( package, szWriteIniValues, uirow );
4251 msiobj_release( &uirow->hdr );
4253 msi_free(fullname);
4254 msi_free(deformated_key);
4255 msi_free(deformated_value);
4256 msi_free(deformated_section);
4257 return ERROR_SUCCESS;
4260 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4262 static const WCHAR query[] = {
4263 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4264 '`','I','n','i','F','i','l','e','`',0};
4265 MSIQUERY *view;
4266 UINT rc;
4268 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4269 if (rc != ERROR_SUCCESS)
4270 return ERROR_SUCCESS;
4272 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4273 msiobj_release(&view->hdr);
4274 return rc;
4277 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4279 MSIPACKAGE *package = param;
4280 LPCWSTR component, section, key, value, identifier;
4281 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4282 MSICOMPONENT *comp;
4283 MSIRECORD *uirow;
4284 INT action;
4286 component = MSI_RecordGetString( row, 8 );
4287 comp = msi_get_loaded_component( package, component );
4288 if (!comp)
4289 return ERROR_SUCCESS;
4291 comp->Action = msi_get_component_action( package, comp );
4292 if (comp->Action != INSTALLSTATE_ABSENT)
4294 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4295 return ERROR_SUCCESS;
4298 identifier = MSI_RecordGetString( row, 1 );
4299 section = MSI_RecordGetString( row, 4 );
4300 key = MSI_RecordGetString( row, 5 );
4301 value = MSI_RecordGetString( row, 6 );
4302 action = MSI_RecordGetInteger( row, 7 );
4304 deformat_string( package, section, &deformated_section );
4305 deformat_string( package, key, &deformated_key );
4306 deformat_string( package, value, &deformated_value );
4308 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4310 filename = get_ini_file_name( package, row );
4312 TRACE("Removing key %s from section %s in %s\n",
4313 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4315 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4317 WARN("Unable to remove key %u\n", GetLastError());
4319 msi_free( filename );
4321 else
4322 FIXME("Unsupported action %d\n", action);
4325 uirow = MSI_CreateRecord( 4 );
4326 MSI_RecordSetStringW( uirow, 1, identifier );
4327 MSI_RecordSetStringW( uirow, 2, deformated_section );
4328 MSI_RecordSetStringW( uirow, 3, deformated_key );
4329 MSI_RecordSetStringW( uirow, 4, deformated_value );
4330 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4331 msiobj_release( &uirow->hdr );
4333 msi_free( deformated_key );
4334 msi_free( deformated_value );
4335 msi_free( deformated_section );
4336 return ERROR_SUCCESS;
4339 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4341 MSIPACKAGE *package = param;
4342 LPCWSTR component, section, key, value, identifier;
4343 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4344 MSICOMPONENT *comp;
4345 MSIRECORD *uirow;
4346 INT action;
4348 component = MSI_RecordGetString( row, 8 );
4349 comp = msi_get_loaded_component( package, component );
4350 if (!comp)
4351 return ERROR_SUCCESS;
4353 comp->Action = msi_get_component_action( package, comp );
4354 if (comp->Action != INSTALLSTATE_LOCAL)
4356 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4357 return ERROR_SUCCESS;
4360 identifier = MSI_RecordGetString( row, 1 );
4361 section = MSI_RecordGetString( row, 4 );
4362 key = MSI_RecordGetString( row, 5 );
4363 value = MSI_RecordGetString( row, 6 );
4364 action = MSI_RecordGetInteger( row, 7 );
4366 deformat_string( package, section, &deformated_section );
4367 deformat_string( package, key, &deformated_key );
4368 deformat_string( package, value, &deformated_value );
4370 if (action == msidbIniFileActionRemoveLine)
4372 filename = get_ini_file_name( package, row );
4374 TRACE("Removing key %s from section %s in %s\n",
4375 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4377 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4379 WARN("Unable to remove key %u\n", GetLastError());
4381 msi_free( filename );
4383 else
4384 FIXME("Unsupported action %d\n", action);
4386 uirow = MSI_CreateRecord( 4 );
4387 MSI_RecordSetStringW( uirow, 1, identifier );
4388 MSI_RecordSetStringW( uirow, 2, deformated_section );
4389 MSI_RecordSetStringW( uirow, 3, deformated_key );
4390 MSI_RecordSetStringW( uirow, 4, deformated_value );
4391 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4392 msiobj_release( &uirow->hdr );
4394 msi_free( deformated_key );
4395 msi_free( deformated_value );
4396 msi_free( deformated_section );
4397 return ERROR_SUCCESS;
4400 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4402 static const WCHAR query[] = {
4403 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4404 '`','I','n','i','F','i','l','e','`',0};
4405 static const WCHAR remove_query[] = {
4406 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4407 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4408 MSIQUERY *view;
4409 UINT rc;
4411 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4412 if (rc == ERROR_SUCCESS)
4414 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4415 msiobj_release( &view->hdr );
4416 if (rc != ERROR_SUCCESS)
4417 return rc;
4419 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4420 if (rc == ERROR_SUCCESS)
4422 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4423 msiobj_release( &view->hdr );
4424 if (rc != ERROR_SUCCESS)
4425 return rc;
4427 return ERROR_SUCCESS;
4430 static void register_dll( const WCHAR *dll, BOOL unregister )
4432 HMODULE hmod;
4434 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4435 if (hmod)
4437 HRESULT (WINAPI *func_ptr)( void );
4438 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4440 func_ptr = (void *)GetProcAddress( hmod, func );
4441 if (func_ptr)
4443 HRESULT hr = func_ptr();
4444 if (FAILED( hr ))
4445 WARN("failed to register dll 0x%08x\n", hr);
4447 else
4448 WARN("entry point %s not found\n", func);
4449 FreeLibrary( hmod );
4450 return;
4452 WARN("failed to load library %u\n", GetLastError());
4455 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4457 MSIPACKAGE *package = param;
4458 LPCWSTR filename;
4459 MSIFILE *file;
4460 MSIRECORD *uirow;
4462 filename = MSI_RecordGetString( row, 1 );
4463 file = msi_get_loaded_file( package, filename );
4464 if (!file)
4466 WARN("unable to find file %s\n", debugstr_w(filename));
4467 return ERROR_SUCCESS;
4469 file->Component->Action = msi_get_component_action( package, file->Component );
4470 if (file->Component->Action != INSTALLSTATE_LOCAL)
4472 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4473 return ERROR_SUCCESS;
4476 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4477 register_dll( file->TargetPath, FALSE );
4479 uirow = MSI_CreateRecord( 2 );
4480 MSI_RecordSetStringW( uirow, 1, file->File );
4481 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4482 msi_ui_actiondata( package, szSelfRegModules, uirow );
4483 msiobj_release( &uirow->hdr );
4485 return ERROR_SUCCESS;
4488 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4490 static const WCHAR query[] = {
4491 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4492 '`','S','e','l','f','R','e','g','`',0};
4493 MSIQUERY *view;
4494 UINT rc;
4496 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4497 if (rc != ERROR_SUCCESS)
4498 return ERROR_SUCCESS;
4500 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4501 msiobj_release(&view->hdr);
4502 return rc;
4505 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4507 MSIPACKAGE *package = param;
4508 LPCWSTR filename;
4509 MSIFILE *file;
4510 MSIRECORD *uirow;
4512 filename = MSI_RecordGetString( row, 1 );
4513 file = msi_get_loaded_file( package, filename );
4514 if (!file)
4516 WARN("unable to find file %s\n", debugstr_w(filename));
4517 return ERROR_SUCCESS;
4519 file->Component->Action = msi_get_component_action( package, file->Component );
4520 if (file->Component->Action != INSTALLSTATE_ABSENT)
4522 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4523 return ERROR_SUCCESS;
4526 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4527 register_dll( file->TargetPath, TRUE );
4529 uirow = MSI_CreateRecord( 2 );
4530 MSI_RecordSetStringW( uirow, 1, file->File );
4531 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4532 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4533 msiobj_release( &uirow->hdr );
4535 return ERROR_SUCCESS;
4538 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4540 static const WCHAR query[] = {
4541 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4542 '`','S','e','l','f','R','e','g','`',0};
4543 MSIQUERY *view;
4544 UINT rc;
4546 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4547 if (rc != ERROR_SUCCESS)
4548 return ERROR_SUCCESS;
4550 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4551 msiobj_release( &view->hdr );
4552 return rc;
4555 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4557 MSIFEATURE *feature;
4558 UINT rc;
4559 HKEY hkey = NULL, userdata = NULL;
4561 if (!msi_check_publish(package))
4562 return ERROR_SUCCESS;
4564 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4565 &hkey, TRUE);
4566 if (rc != ERROR_SUCCESS)
4567 goto end;
4569 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4570 &userdata, TRUE);
4571 if (rc != ERROR_SUCCESS)
4572 goto end;
4574 /* here the guids are base 85 encoded */
4575 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4577 ComponentList *cl;
4578 LPWSTR data = NULL;
4579 GUID clsid;
4580 INT size;
4581 BOOL absent = FALSE;
4582 MSIRECORD *uirow;
4584 if (feature->Action != INSTALLSTATE_LOCAL &&
4585 feature->Action != INSTALLSTATE_SOURCE &&
4586 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4588 size = 1;
4589 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4591 size += 21;
4593 if (feature->Feature_Parent)
4594 size += strlenW( feature->Feature_Parent )+2;
4596 data = msi_alloc(size * sizeof(WCHAR));
4598 data[0] = 0;
4599 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4601 MSICOMPONENT* component = cl->component;
4602 WCHAR buf[21];
4604 buf[0] = 0;
4605 if (component->ComponentId)
4607 TRACE("From %s\n",debugstr_w(component->ComponentId));
4608 CLSIDFromString(component->ComponentId, &clsid);
4609 encode_base85_guid(&clsid,buf);
4610 TRACE("to %s\n",debugstr_w(buf));
4611 strcatW(data,buf);
4615 if (feature->Feature_Parent)
4617 static const WCHAR sep[] = {'\2',0};
4618 strcatW(data,sep);
4619 strcatW(data,feature->Feature_Parent);
4622 msi_reg_set_val_str( userdata, feature->Feature, data );
4623 msi_free(data);
4625 size = 0;
4626 if (feature->Feature_Parent)
4627 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4628 if (!absent)
4630 size += sizeof(WCHAR);
4631 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4632 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4634 else
4636 size += 2*sizeof(WCHAR);
4637 data = msi_alloc(size);
4638 data[0] = 0x6;
4639 data[1] = 0;
4640 if (feature->Feature_Parent)
4641 strcpyW( &data[1], feature->Feature_Parent );
4642 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4643 (LPBYTE)data,size);
4644 msi_free(data);
4647 /* the UI chunk */
4648 uirow = MSI_CreateRecord( 1 );
4649 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4650 msi_ui_actiondata( package, szPublishFeatures, uirow );
4651 msiobj_release( &uirow->hdr );
4652 /* FIXME: call msi_ui_progress? */
4655 end:
4656 RegCloseKey(hkey);
4657 RegCloseKey(userdata);
4658 return rc;
4661 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4663 UINT r;
4664 HKEY hkey;
4665 MSIRECORD *uirow;
4667 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4669 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4670 &hkey, FALSE);
4671 if (r == ERROR_SUCCESS)
4673 RegDeleteValueW(hkey, feature->Feature);
4674 RegCloseKey(hkey);
4677 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4678 &hkey, FALSE);
4679 if (r == ERROR_SUCCESS)
4681 RegDeleteValueW(hkey, feature->Feature);
4682 RegCloseKey(hkey);
4685 uirow = MSI_CreateRecord( 1 );
4686 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4687 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4688 msiobj_release( &uirow->hdr );
4690 return ERROR_SUCCESS;
4693 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4695 MSIFEATURE *feature;
4697 if (!msi_check_unpublish(package))
4698 return ERROR_SUCCESS;
4700 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4702 msi_unpublish_feature(package, feature);
4705 return ERROR_SUCCESS;
4708 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4710 SYSTEMTIME systime;
4711 DWORD size, langid;
4712 WCHAR date[9], *val, *buffer;
4713 const WCHAR *prop, *key;
4715 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4716 static const WCHAR modpath_fmt[] =
4717 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4718 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4719 static const WCHAR szModifyPath[] =
4720 {'M','o','d','i','f','y','P','a','t','h',0};
4721 static const WCHAR szUninstallString[] =
4722 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4723 static const WCHAR szEstimatedSize[] =
4724 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4725 static const WCHAR szDisplayVersion[] =
4726 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4727 static const WCHAR szInstallSource[] =
4728 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4729 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4730 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4731 static const WCHAR szAuthorizedCDFPrefix[] =
4732 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4733 static const WCHAR szARPCONTACT[] =
4734 {'A','R','P','C','O','N','T','A','C','T',0};
4735 static const WCHAR szContact[] =
4736 {'C','o','n','t','a','c','t',0};
4737 static const WCHAR szARPCOMMENTS[] =
4738 {'A','R','P','C','O','M','M','E','N','T','S',0};
4739 static const WCHAR szComments[] =
4740 {'C','o','m','m','e','n','t','s',0};
4741 static const WCHAR szProductName[] =
4742 {'P','r','o','d','u','c','t','N','a','m','e',0};
4743 static const WCHAR szDisplayName[] =
4744 {'D','i','s','p','l','a','y','N','a','m','e',0};
4745 static const WCHAR szARPHELPLINK[] =
4746 {'A','R','P','H','E','L','P','L','I','N','K',0};
4747 static const WCHAR szHelpLink[] =
4748 {'H','e','l','p','L','i','n','k',0};
4749 static const WCHAR szARPHELPTELEPHONE[] =
4750 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4751 static const WCHAR szHelpTelephone[] =
4752 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4753 static const WCHAR szARPINSTALLLOCATION[] =
4754 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4755 static const WCHAR szInstallLocation[] =
4756 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4757 static const WCHAR szManufacturer[] =
4758 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4759 static const WCHAR szPublisher[] =
4760 {'P','u','b','l','i','s','h','e','r',0};
4761 static const WCHAR szARPREADME[] =
4762 {'A','R','P','R','E','A','D','M','E',0};
4763 static const WCHAR szReadme[] =
4764 {'R','e','a','d','M','e',0};
4765 static const WCHAR szARPSIZE[] =
4766 {'A','R','P','S','I','Z','E',0};
4767 static const WCHAR szSize[] =
4768 {'S','i','z','e',0};
4769 static const WCHAR szARPURLINFOABOUT[] =
4770 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4771 static const WCHAR szURLInfoAbout[] =
4772 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4773 static const WCHAR szARPURLUPDATEINFO[] =
4774 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4775 static const WCHAR szURLUpdateInfo[] =
4776 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4777 static const WCHAR szARPSYSTEMCOMPONENT[] =
4778 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4779 static const WCHAR szSystemComponent[] =
4780 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4782 static const WCHAR *propval[] = {
4783 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4784 szARPCONTACT, szContact,
4785 szARPCOMMENTS, szComments,
4786 szProductName, szDisplayName,
4787 szARPHELPLINK, szHelpLink,
4788 szARPHELPTELEPHONE, szHelpTelephone,
4789 szARPINSTALLLOCATION, szInstallLocation,
4790 szSourceDir, szInstallSource,
4791 szManufacturer, szPublisher,
4792 szARPREADME, szReadme,
4793 szARPSIZE, szSize,
4794 szARPURLINFOABOUT, szURLInfoAbout,
4795 szARPURLUPDATEINFO, szURLUpdateInfo,
4796 NULL
4798 const WCHAR **p = propval;
4800 while (*p)
4802 prop = *p++;
4803 key = *p++;
4804 val = msi_dup_property(package->db, prop);
4805 msi_reg_set_val_str(hkey, key, val);
4806 msi_free(val);
4809 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4810 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4812 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4814 size = deformat_string(package, modpath_fmt, &buffer);
4815 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4816 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4817 msi_free(buffer);
4819 /* FIXME: Write real Estimated Size when we have it */
4820 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4822 GetLocalTime(&systime);
4823 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4824 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4826 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4827 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4829 buffer = msi_dup_property(package->db, szProductVersion);
4830 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4831 if (buffer)
4833 DWORD verdword = msi_version_str_to_dword(buffer);
4835 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4836 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4837 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4838 msi_free(buffer);
4841 return ERROR_SUCCESS;
4844 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4846 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4847 MSIRECORD *uirow;
4848 LPWSTR upgrade_code;
4849 HKEY hkey, props, upgrade_key;
4850 UINT rc;
4852 /* FIXME: also need to publish if the product is in advertise mode */
4853 if (!msi_check_publish(package))
4854 return ERROR_SUCCESS;
4856 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4857 if (rc != ERROR_SUCCESS)
4858 return rc;
4860 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4861 if (rc != ERROR_SUCCESS)
4862 goto done;
4864 rc = msi_publish_install_properties(package, hkey);
4865 if (rc != ERROR_SUCCESS)
4866 goto done;
4868 rc = msi_publish_install_properties(package, props);
4869 if (rc != ERROR_SUCCESS)
4870 goto done;
4872 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4873 if (upgrade_code)
4875 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4876 if (rc == ERROR_SUCCESS)
4878 squash_guid( package->ProductCode, squashed_pc );
4879 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4880 RegCloseKey( upgrade_key );
4882 msi_free( upgrade_code );
4884 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4885 package->delete_on_close = FALSE;
4887 done:
4888 uirow = MSI_CreateRecord( 1 );
4889 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4890 msi_ui_actiondata( package, szRegisterProduct, uirow );
4891 msiobj_release( &uirow->hdr );
4893 RegCloseKey(hkey);
4894 return ERROR_SUCCESS;
4897 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4899 return execute_script(package, SCRIPT_INSTALL);
4902 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4904 MSIPACKAGE *package = param;
4905 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4906 WCHAR *p, *icon_path;
4908 if (!icon) return ERROR_SUCCESS;
4909 if ((icon_path = msi_build_icon_path( package, icon )))
4911 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4912 DeleteFileW( icon_path );
4913 if ((p = strrchrW( icon_path, '\\' )))
4915 *p = 0;
4916 RemoveDirectoryW( icon_path );
4918 msi_free( icon_path );
4920 return ERROR_SUCCESS;
4923 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4925 static const WCHAR query[]= {
4926 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4927 MSIQUERY *view;
4928 UINT r;
4930 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4931 if (r == ERROR_SUCCESS)
4933 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4934 msiobj_release( &view->hdr );
4935 if (r != ERROR_SUCCESS)
4936 return r;
4938 return ERROR_SUCCESS;
4941 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4943 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4944 WCHAR *upgrade, **features;
4945 BOOL full_uninstall = TRUE;
4946 MSIFEATURE *feature;
4947 MSIPATCHINFO *patch;
4948 UINT i;
4950 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4952 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4954 features = msi_split_string( remove, ',' );
4955 for (i = 0; features && features[i]; i++)
4957 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4959 msi_free(features);
4961 if (!full_uninstall)
4962 return ERROR_SUCCESS;
4964 MSIREG_DeleteProductKey(package->ProductCode);
4965 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4966 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4968 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4969 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4970 MSIREG_DeleteUserProductKey(package->ProductCode);
4971 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4973 upgrade = msi_dup_property(package->db, szUpgradeCode);
4974 if (upgrade)
4976 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4977 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4978 msi_free(upgrade);
4981 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4983 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4984 if (!strcmpW( package->ProductCode, patch->products ))
4986 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
4987 patch->delete_on_close = TRUE;
4989 /* FIXME: remove local patch package if this is the last product */
4991 TRACE("removing local package %s\n", debugstr_w(package->localfile));
4992 package->delete_on_close = TRUE;
4994 msi_unpublish_icons( package );
4995 return ERROR_SUCCESS;
4998 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5000 UINT rc;
5001 WCHAR *remove;
5003 /* turn off scheduling */
5004 package->script->CurrentlyScripting= FALSE;
5006 /* first do the same as an InstallExecute */
5007 rc = ACTION_InstallExecute(package);
5008 if (rc != ERROR_SUCCESS)
5009 return rc;
5011 /* then handle commit actions */
5012 rc = execute_script(package, SCRIPT_COMMIT);
5013 if (rc != ERROR_SUCCESS)
5014 return rc;
5016 remove = msi_dup_property(package->db, szRemove);
5017 rc = msi_unpublish_product(package, remove);
5018 msi_free(remove);
5019 return rc;
5022 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5024 static const WCHAR RunOnce[] = {
5025 'S','o','f','t','w','a','r','e','\\',
5026 'M','i','c','r','o','s','o','f','t','\\',
5027 'W','i','n','d','o','w','s','\\',
5028 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5029 'R','u','n','O','n','c','e',0};
5030 static const WCHAR InstallRunOnce[] = {
5031 'S','o','f','t','w','a','r','e','\\',
5032 'M','i','c','r','o','s','o','f','t','\\',
5033 'W','i','n','d','o','w','s','\\',
5034 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5035 'I','n','s','t','a','l','l','e','r','\\',
5036 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5038 static const WCHAR msiexec_fmt[] = {
5039 '%','s',
5040 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5041 '\"','%','s','\"',0};
5042 static const WCHAR install_fmt[] = {
5043 '/','I',' ','\"','%','s','\"',' ',
5044 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5045 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5046 WCHAR buffer[256], sysdir[MAX_PATH];
5047 HKEY hkey;
5048 WCHAR squished_pc[100];
5050 squash_guid(package->ProductCode,squished_pc);
5052 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5053 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5054 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5055 squished_pc);
5057 msi_reg_set_val_str( hkey, squished_pc, buffer );
5058 RegCloseKey(hkey);
5060 TRACE("Reboot command %s\n",debugstr_w(buffer));
5062 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5063 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5065 msi_reg_set_val_str( hkey, squished_pc, buffer );
5066 RegCloseKey(hkey);
5068 return ERROR_INSTALL_SUSPEND;
5071 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5073 static const WCHAR query[] =
5074 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5075 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5076 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5077 MSIRECORD *rec, *row;
5078 DWORD i, size = 0;
5079 va_list va;
5080 const WCHAR *str;
5081 WCHAR *data;
5083 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5085 rec = MSI_CreateRecord( count + 2 );
5086 str = MSI_RecordGetString( row, 1 );
5087 MSI_RecordSetStringW( rec, 0, str );
5088 msiobj_release( &row->hdr );
5089 MSI_RecordSetInteger( rec, 1, error );
5091 va_start( va, count );
5092 for (i = 0; i < count; i++)
5094 str = va_arg( va, const WCHAR *);
5095 MSI_RecordSetStringW( rec, i + 2, str );
5097 va_end( va );
5099 MSI_FormatRecordW( package, rec, NULL, &size );
5100 size++;
5101 data = msi_alloc( size * sizeof(WCHAR) );
5102 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5103 else data[0] = 0;
5104 msiobj_release( &rec->hdr );
5105 return data;
5108 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5110 DWORD attrib;
5111 UINT rc;
5114 * We are currently doing what should be done here in the top level Install
5115 * however for Administrative and uninstalls this step will be needed
5117 if (!package->PackagePath)
5118 return ERROR_SUCCESS;
5120 msi_set_sourcedir_props(package, TRUE);
5122 attrib = GetFileAttributesW(package->db->path);
5123 if (attrib == INVALID_FILE_ATTRIBUTES)
5125 LPWSTR prompt, msg;
5126 DWORD size = 0;
5128 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5129 package->Context, MSICODE_PRODUCT,
5130 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5131 if (rc == ERROR_MORE_DATA)
5133 prompt = msi_alloc(size * sizeof(WCHAR));
5134 MsiSourceListGetInfoW(package->ProductCode, NULL,
5135 package->Context, MSICODE_PRODUCT,
5136 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5138 else
5139 prompt = strdupW(package->db->path);
5141 msg = msi_build_error_string(package, 1302, 1, prompt);
5142 msi_free(prompt);
5143 while(attrib == INVALID_FILE_ATTRIBUTES)
5145 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5146 if (rc == IDCANCEL)
5148 msi_free(msg);
5149 return ERROR_INSTALL_USEREXIT;
5151 attrib = GetFileAttributesW(package->db->path);
5153 msi_free(msg);
5154 rc = ERROR_SUCCESS;
5156 else
5157 return ERROR_SUCCESS;
5159 return rc;
5162 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5164 HKEY hkey = 0;
5165 LPWSTR buffer, productid = NULL;
5166 UINT i, rc = ERROR_SUCCESS;
5167 MSIRECORD *uirow;
5169 static const WCHAR szPropKeys[][80] =
5171 {'P','r','o','d','u','c','t','I','D',0},
5172 {'U','S','E','R','N','A','M','E',0},
5173 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5174 {0},
5177 static const WCHAR szRegKeys[][80] =
5179 {'P','r','o','d','u','c','t','I','D',0},
5180 {'R','e','g','O','w','n','e','r',0},
5181 {'R','e','g','C','o','m','p','a','n','y',0},
5182 {0},
5185 if (msi_check_unpublish(package))
5187 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5188 goto end;
5191 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5192 if (!productid)
5193 goto end;
5195 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5196 NULL, &hkey, TRUE);
5197 if (rc != ERROR_SUCCESS)
5198 goto end;
5200 for( i = 0; szPropKeys[i][0]; i++ )
5202 buffer = msi_dup_property( package->db, szPropKeys[i] );
5203 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5204 msi_free( buffer );
5207 end:
5208 uirow = MSI_CreateRecord( 1 );
5209 MSI_RecordSetStringW( uirow, 1, productid );
5210 msi_ui_actiondata( package, szRegisterUser, uirow );
5211 msiobj_release( &uirow->hdr );
5213 msi_free(productid);
5214 RegCloseKey(hkey);
5215 return rc;
5219 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5221 UINT rc;
5223 package->script->InWhatSequence |= SEQUENCE_EXEC;
5224 rc = ACTION_ProcessExecSequence(package,FALSE);
5225 return rc;
5228 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5230 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5231 WCHAR productid_85[21], component_85[21], *ret;
5232 GUID clsid;
5233 DWORD sz;
5235 /* > is used if there is a component GUID and < if not. */
5237 productid_85[0] = 0;
5238 component_85[0] = 0;
5239 CLSIDFromString( package->ProductCode, &clsid );
5241 encode_base85_guid( &clsid, productid_85 );
5242 if (component)
5244 CLSIDFromString( component->ComponentId, &clsid );
5245 encode_base85_guid( &clsid, component_85 );
5248 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5249 debugstr_w(component_85));
5251 sz = 20 + strlenW( feature ) + 20 + 3;
5252 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5253 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5254 return ret;
5257 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5259 MSIPACKAGE *package = param;
5260 LPCWSTR compgroupid, component, feature, qualifier, text;
5261 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5262 HKEY hkey = NULL;
5263 UINT rc;
5264 MSICOMPONENT *comp;
5265 MSIFEATURE *feat;
5266 DWORD sz;
5267 MSIRECORD *uirow;
5268 int len;
5270 feature = MSI_RecordGetString(rec, 5);
5271 feat = msi_get_loaded_feature(package, feature);
5272 if (!feat)
5273 return ERROR_SUCCESS;
5275 feat->Action = msi_get_feature_action( package, feat );
5276 if (feat->Action != INSTALLSTATE_LOCAL &&
5277 feat->Action != INSTALLSTATE_SOURCE &&
5278 feat->Action != INSTALLSTATE_ADVERTISED)
5280 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5281 return ERROR_SUCCESS;
5284 component = MSI_RecordGetString(rec, 3);
5285 comp = msi_get_loaded_component(package, component);
5286 if (!comp)
5287 return ERROR_SUCCESS;
5289 compgroupid = MSI_RecordGetString(rec,1);
5290 qualifier = MSI_RecordGetString(rec,2);
5292 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5293 if (rc != ERROR_SUCCESS)
5294 goto end;
5296 advertise = msi_create_component_advertise_string( package, comp, feature );
5297 text = MSI_RecordGetString( rec, 4 );
5298 if (text)
5300 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5301 strcpyW( p, advertise );
5302 strcatW( p, text );
5303 msi_free( advertise );
5304 advertise = p;
5306 existing = msi_reg_get_val_str( hkey, qualifier );
5308 sz = strlenW( advertise ) + 1;
5309 if (existing)
5311 for (p = existing; *p; p += len)
5313 len = strlenW( p ) + 1;
5314 if (strcmpW( advertise, p )) sz += len;
5317 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5319 rc = ERROR_OUTOFMEMORY;
5320 goto end;
5322 q = output;
5323 if (existing)
5325 for (p = existing; *p; p += len)
5327 len = strlenW( p ) + 1;
5328 if (strcmpW( advertise, p ))
5330 memcpy( q, p, len * sizeof(WCHAR) );
5331 q += len;
5335 strcpyW( q, advertise );
5336 q[strlenW( q ) + 1] = 0;
5338 msi_reg_set_val_multi_str( hkey, qualifier, output );
5340 end:
5341 RegCloseKey(hkey);
5342 msi_free( output );
5343 msi_free( advertise );
5344 msi_free( existing );
5346 /* the UI chunk */
5347 uirow = MSI_CreateRecord( 2 );
5348 MSI_RecordSetStringW( uirow, 1, compgroupid );
5349 MSI_RecordSetStringW( uirow, 2, qualifier);
5350 msi_ui_actiondata( package, szPublishComponents, uirow );
5351 msiobj_release( &uirow->hdr );
5352 /* FIXME: call ui_progress? */
5354 return rc;
5358 * At present I am ignorning the advertised components part of this and only
5359 * focusing on the qualified component sets
5361 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5363 static const WCHAR query[] = {
5364 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5365 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5366 MSIQUERY *view;
5367 UINT rc;
5369 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5370 if (rc != ERROR_SUCCESS)
5371 return ERROR_SUCCESS;
5373 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5374 msiobj_release(&view->hdr);
5375 return rc;
5378 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5380 static const WCHAR szInstallerComponents[] = {
5381 'S','o','f','t','w','a','r','e','\\',
5382 'M','i','c','r','o','s','o','f','t','\\',
5383 'I','n','s','t','a','l','l','e','r','\\',
5384 'C','o','m','p','o','n','e','n','t','s','\\',0};
5386 MSIPACKAGE *package = param;
5387 LPCWSTR compgroupid, component, feature, qualifier;
5388 MSICOMPONENT *comp;
5389 MSIFEATURE *feat;
5390 MSIRECORD *uirow;
5391 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5392 LONG res;
5394 feature = MSI_RecordGetString( rec, 5 );
5395 feat = msi_get_loaded_feature( package, feature );
5396 if (!feat)
5397 return ERROR_SUCCESS;
5399 feat->Action = msi_get_feature_action( package, feat );
5400 if (feat->Action != INSTALLSTATE_ABSENT)
5402 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5403 return ERROR_SUCCESS;
5406 component = MSI_RecordGetString( rec, 3 );
5407 comp = msi_get_loaded_component( package, component );
5408 if (!comp)
5409 return ERROR_SUCCESS;
5411 compgroupid = MSI_RecordGetString( rec, 1 );
5412 qualifier = MSI_RecordGetString( rec, 2 );
5414 squash_guid( compgroupid, squashed );
5415 strcpyW( keypath, szInstallerComponents );
5416 strcatW( keypath, squashed );
5418 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5419 if (res != ERROR_SUCCESS)
5421 WARN("Unable to delete component key %d\n", res);
5424 uirow = MSI_CreateRecord( 2 );
5425 MSI_RecordSetStringW( uirow, 1, compgroupid );
5426 MSI_RecordSetStringW( uirow, 2, qualifier );
5427 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5428 msiobj_release( &uirow->hdr );
5430 return ERROR_SUCCESS;
5433 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5435 static const WCHAR query[] = {
5436 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5437 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5438 MSIQUERY *view;
5439 UINT rc;
5441 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5442 if (rc != ERROR_SUCCESS)
5443 return ERROR_SUCCESS;
5445 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5446 msiobj_release( &view->hdr );
5447 return rc;
5450 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5452 static const WCHAR query[] =
5453 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5454 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5455 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5456 MSIPACKAGE *package = param;
5457 MSICOMPONENT *component;
5458 MSIRECORD *row;
5459 MSIFILE *file;
5460 SC_HANDLE hscm = NULL, service = NULL;
5461 LPCWSTR comp, key;
5462 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5463 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5464 DWORD serv_type, start_type, err_control;
5465 SERVICE_DESCRIPTIONW sd = {NULL};
5467 comp = MSI_RecordGetString( rec, 12 );
5468 component = msi_get_loaded_component( package, comp );
5469 if (!component)
5471 WARN("service component not found\n");
5472 goto done;
5474 component->Action = msi_get_component_action( package, component );
5475 if (component->Action != INSTALLSTATE_LOCAL)
5477 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5478 goto done;
5480 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5481 if (!hscm)
5483 ERR("Failed to open the SC Manager!\n");
5484 goto done;
5487 start_type = MSI_RecordGetInteger(rec, 5);
5488 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5489 goto done;
5491 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5492 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5493 serv_type = MSI_RecordGetInteger(rec, 4);
5494 err_control = MSI_RecordGetInteger(rec, 6);
5495 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5496 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5497 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5498 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5499 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5500 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5502 /* fetch the service path */
5503 row = MSI_QueryGetRecord(package->db, query, comp);
5504 if (!row)
5506 ERR("Query failed\n");
5507 goto done;
5509 key = MSI_RecordGetString(row, 6);
5510 file = msi_get_loaded_file(package, key);
5511 msiobj_release(&row->hdr);
5512 if (!file)
5514 ERR("Failed to load the service file\n");
5515 goto done;
5518 if (!args || !args[0]) image_path = file->TargetPath;
5519 else
5521 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5522 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5523 return ERROR_OUTOFMEMORY;
5525 strcpyW(image_path, file->TargetPath);
5526 strcatW(image_path, szSpace);
5527 strcatW(image_path, args);
5529 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5530 start_type, err_control, image_path, load_order,
5531 NULL, depends, serv_name, pass);
5533 if (!service)
5535 if (GetLastError() != ERROR_SERVICE_EXISTS)
5536 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5538 else if (sd.lpDescription)
5540 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5541 WARN("failed to set service description %u\n", GetLastError());
5544 if (image_path != file->TargetPath) msi_free(image_path);
5545 done:
5546 CloseServiceHandle(service);
5547 CloseServiceHandle(hscm);
5548 msi_free(name);
5549 msi_free(disp);
5550 msi_free(sd.lpDescription);
5551 msi_free(load_order);
5552 msi_free(serv_name);
5553 msi_free(pass);
5554 msi_free(depends);
5555 msi_free(args);
5557 return ERROR_SUCCESS;
5560 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5562 static const WCHAR query[] = {
5563 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5564 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5565 MSIQUERY *view;
5566 UINT rc;
5568 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5569 if (rc != ERROR_SUCCESS)
5570 return ERROR_SUCCESS;
5572 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5573 msiobj_release(&view->hdr);
5574 return rc;
5577 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5578 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5580 LPCWSTR *vector, *temp_vector;
5581 LPWSTR p, q;
5582 DWORD sep_len;
5584 static const WCHAR separator[] = {'[','~',']',0};
5586 *numargs = 0;
5587 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5589 if (!args)
5590 return NULL;
5592 vector = msi_alloc(sizeof(LPWSTR));
5593 if (!vector)
5594 return NULL;
5596 p = args;
5599 (*numargs)++;
5600 vector[*numargs - 1] = p;
5602 if ((q = strstrW(p, separator)))
5604 *q = '\0';
5606 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5607 if (!temp_vector)
5609 msi_free(vector);
5610 return NULL;
5612 vector = temp_vector;
5614 p = q + sep_len;
5616 } while (q);
5618 return vector;
5621 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5623 MSIPACKAGE *package = param;
5624 MSICOMPONENT *comp;
5625 MSIRECORD *uirow;
5626 SC_HANDLE scm = NULL, service = NULL;
5627 LPCWSTR component, *vector = NULL;
5628 LPWSTR name, args, display_name = NULL;
5629 DWORD event, numargs, len, wait, dummy;
5630 UINT r = ERROR_FUNCTION_FAILED;
5631 SERVICE_STATUS_PROCESS status;
5632 ULONGLONG start_time;
5634 component = MSI_RecordGetString(rec, 6);
5635 comp = msi_get_loaded_component(package, component);
5636 if (!comp)
5637 return ERROR_SUCCESS;
5639 comp->Action = msi_get_component_action( package, comp );
5640 if (comp->Action != INSTALLSTATE_LOCAL)
5642 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5643 return ERROR_SUCCESS;
5646 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5647 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5648 event = MSI_RecordGetInteger(rec, 3);
5649 wait = MSI_RecordGetInteger(rec, 5);
5651 if (!(event & msidbServiceControlEventStart))
5653 r = ERROR_SUCCESS;
5654 goto done;
5657 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5658 if (!scm)
5660 ERR("Failed to open the service control manager\n");
5661 goto done;
5664 len = 0;
5665 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5666 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5668 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5669 GetServiceDisplayNameW( scm, name, display_name, &len );
5672 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5673 if (!service)
5675 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5676 goto done;
5679 vector = msi_service_args_to_vector(args, &numargs);
5681 if (!StartServiceW(service, numargs, vector) &&
5682 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5684 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5685 goto done;
5688 r = ERROR_SUCCESS;
5689 if (wait)
5691 /* wait for at most 30 seconds for the service to be up and running */
5692 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5693 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5695 TRACE("failed to query service status (%u)\n", GetLastError());
5696 goto done;
5698 start_time = GetTickCount64();
5699 while (status.dwCurrentState == SERVICE_START_PENDING)
5701 if (GetTickCount64() - start_time > 30000) break;
5702 Sleep(1000);
5703 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5704 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5706 TRACE("failed to query service status (%u)\n", GetLastError());
5707 goto done;
5710 if (status.dwCurrentState != SERVICE_RUNNING)
5712 WARN("service failed to start %u\n", status.dwCurrentState);
5713 r = ERROR_FUNCTION_FAILED;
5717 done:
5718 uirow = MSI_CreateRecord( 2 );
5719 MSI_RecordSetStringW( uirow, 1, display_name );
5720 MSI_RecordSetStringW( uirow, 2, name );
5721 msi_ui_actiondata( package, szStartServices, uirow );
5722 msiobj_release( &uirow->hdr );
5724 CloseServiceHandle(service);
5725 CloseServiceHandle(scm);
5727 msi_free(name);
5728 msi_free(args);
5729 msi_free(vector);
5730 msi_free(display_name);
5731 return r;
5734 static UINT ACTION_StartServices( MSIPACKAGE *package )
5736 static const WCHAR query[] = {
5737 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5738 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5739 MSIQUERY *view;
5740 UINT rc;
5742 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5743 if (rc != ERROR_SUCCESS)
5744 return ERROR_SUCCESS;
5746 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5747 msiobj_release(&view->hdr);
5748 return rc;
5751 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5753 DWORD i, needed, count;
5754 ENUM_SERVICE_STATUSW *dependencies;
5755 SERVICE_STATUS ss;
5756 SC_HANDLE depserv;
5758 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5759 0, &needed, &count))
5760 return TRUE;
5762 if (GetLastError() != ERROR_MORE_DATA)
5763 return FALSE;
5765 dependencies = msi_alloc(needed);
5766 if (!dependencies)
5767 return FALSE;
5769 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5770 needed, &needed, &count))
5771 goto error;
5773 for (i = 0; i < count; i++)
5775 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5776 SERVICE_STOP | SERVICE_QUERY_STATUS);
5777 if (!depserv)
5778 goto error;
5780 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5781 goto error;
5784 return TRUE;
5786 error:
5787 msi_free(dependencies);
5788 return FALSE;
5791 static UINT stop_service( LPCWSTR name )
5793 SC_HANDLE scm = NULL, service = NULL;
5794 SERVICE_STATUS status;
5795 SERVICE_STATUS_PROCESS ssp;
5796 DWORD needed;
5798 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5799 if (!scm)
5801 WARN("Failed to open the SCM: %d\n", GetLastError());
5802 goto done;
5805 service = OpenServiceW(scm, name,
5806 SERVICE_STOP |
5807 SERVICE_QUERY_STATUS |
5808 SERVICE_ENUMERATE_DEPENDENTS);
5809 if (!service)
5811 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5812 goto done;
5815 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5816 sizeof(SERVICE_STATUS_PROCESS), &needed))
5818 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5819 goto done;
5822 if (ssp.dwCurrentState == SERVICE_STOPPED)
5823 goto done;
5825 stop_service_dependents(scm, service);
5827 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5828 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5830 done:
5831 CloseServiceHandle(service);
5832 CloseServiceHandle(scm);
5834 return ERROR_SUCCESS;
5837 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5839 MSIPACKAGE *package = param;
5840 MSICOMPONENT *comp;
5841 MSIRECORD *uirow;
5842 LPCWSTR component;
5843 LPWSTR name = NULL, display_name = NULL;
5844 DWORD event, len;
5845 SC_HANDLE scm;
5847 event = MSI_RecordGetInteger( rec, 3 );
5848 if (!(event & msidbServiceControlEventStop))
5849 return ERROR_SUCCESS;
5851 component = MSI_RecordGetString( rec, 6 );
5852 comp = msi_get_loaded_component( package, component );
5853 if (!comp)
5854 return ERROR_SUCCESS;
5856 comp->Action = msi_get_component_action( package, comp );
5857 if (comp->Action != INSTALLSTATE_ABSENT)
5859 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5860 return ERROR_SUCCESS;
5863 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5864 if (!scm)
5866 ERR("Failed to open the service control manager\n");
5867 goto done;
5870 len = 0;
5871 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5872 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5874 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5875 GetServiceDisplayNameW( scm, name, display_name, &len );
5877 CloseServiceHandle( scm );
5879 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5880 stop_service( name );
5882 done:
5883 uirow = MSI_CreateRecord( 2 );
5884 MSI_RecordSetStringW( uirow, 1, display_name );
5885 MSI_RecordSetStringW( uirow, 2, name );
5886 msi_ui_actiondata( package, szStopServices, uirow );
5887 msiobj_release( &uirow->hdr );
5889 msi_free( name );
5890 msi_free( display_name );
5891 return ERROR_SUCCESS;
5894 static UINT ACTION_StopServices( MSIPACKAGE *package )
5896 static const WCHAR query[] = {
5897 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5898 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5899 MSIQUERY *view;
5900 UINT rc;
5902 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5903 if (rc != ERROR_SUCCESS)
5904 return ERROR_SUCCESS;
5906 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5907 msiobj_release(&view->hdr);
5908 return rc;
5911 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5913 MSIPACKAGE *package = param;
5914 MSICOMPONENT *comp;
5915 MSIRECORD *uirow;
5916 LPCWSTR component;
5917 LPWSTR name = NULL, display_name = NULL;
5918 DWORD event, len;
5919 SC_HANDLE scm = NULL, service = NULL;
5921 event = MSI_RecordGetInteger( rec, 3 );
5922 if (!(event & msidbServiceControlEventDelete))
5923 return ERROR_SUCCESS;
5925 component = MSI_RecordGetString(rec, 6);
5926 comp = msi_get_loaded_component(package, component);
5927 if (!comp)
5928 return ERROR_SUCCESS;
5930 comp->Action = msi_get_component_action( package, comp );
5931 if (comp->Action != INSTALLSTATE_ABSENT)
5933 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5934 return ERROR_SUCCESS;
5937 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5938 stop_service( name );
5940 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5941 if (!scm)
5943 WARN("Failed to open the SCM: %d\n", GetLastError());
5944 goto done;
5947 len = 0;
5948 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5949 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5951 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5952 GetServiceDisplayNameW( scm, name, display_name, &len );
5955 service = OpenServiceW( scm, name, DELETE );
5956 if (!service)
5958 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5959 goto done;
5962 if (!DeleteService( service ))
5963 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5965 done:
5966 uirow = MSI_CreateRecord( 2 );
5967 MSI_RecordSetStringW( uirow, 1, display_name );
5968 MSI_RecordSetStringW( uirow, 2, name );
5969 msi_ui_actiondata( package, szDeleteServices, uirow );
5970 msiobj_release( &uirow->hdr );
5972 CloseServiceHandle( service );
5973 CloseServiceHandle( scm );
5974 msi_free( name );
5975 msi_free( display_name );
5977 return ERROR_SUCCESS;
5980 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5982 static const WCHAR query[] = {
5983 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5984 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5985 MSIQUERY *view;
5986 UINT rc;
5988 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5989 if (rc != ERROR_SUCCESS)
5990 return ERROR_SUCCESS;
5992 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5993 msiobj_release( &view->hdr );
5994 return rc;
5997 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5999 MSIPACKAGE *package = param;
6000 LPWSTR driver, driver_path, ptr;
6001 WCHAR outpath[MAX_PATH];
6002 MSIFILE *driver_file = NULL, *setup_file = NULL;
6003 MSICOMPONENT *comp;
6004 MSIRECORD *uirow;
6005 LPCWSTR desc, file_key, component;
6006 DWORD len, usage;
6007 UINT r = ERROR_SUCCESS;
6009 static const WCHAR driver_fmt[] = {
6010 'D','r','i','v','e','r','=','%','s',0};
6011 static const WCHAR setup_fmt[] = {
6012 'S','e','t','u','p','=','%','s',0};
6013 static const WCHAR usage_fmt[] = {
6014 'F','i','l','e','U','s','a','g','e','=','1',0};
6016 component = MSI_RecordGetString( rec, 2 );
6017 comp = msi_get_loaded_component( package, component );
6018 if (!comp)
6019 return ERROR_SUCCESS;
6021 comp->Action = msi_get_component_action( package, comp );
6022 if (comp->Action != INSTALLSTATE_LOCAL)
6024 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6025 return ERROR_SUCCESS;
6027 desc = MSI_RecordGetString(rec, 3);
6029 file_key = MSI_RecordGetString( rec, 4 );
6030 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6032 file_key = MSI_RecordGetString( rec, 5 );
6033 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6035 if (!driver_file)
6037 ERR("ODBC Driver entry not found!\n");
6038 return ERROR_FUNCTION_FAILED;
6041 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6042 if (setup_file)
6043 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6044 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6046 driver = msi_alloc(len * sizeof(WCHAR));
6047 if (!driver)
6048 return ERROR_OUTOFMEMORY;
6050 ptr = driver;
6051 lstrcpyW(ptr, desc);
6052 ptr += lstrlenW(ptr) + 1;
6054 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6055 ptr += len + 1;
6057 if (setup_file)
6059 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6060 ptr += len + 1;
6063 lstrcpyW(ptr, usage_fmt);
6064 ptr += lstrlenW(ptr) + 1;
6065 *ptr = '\0';
6067 driver_path = strdupW(driver_file->TargetPath);
6068 ptr = strrchrW(driver_path, '\\');
6069 if (ptr) *ptr = '\0';
6071 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6072 NULL, ODBC_INSTALL_COMPLETE, &usage))
6074 ERR("Failed to install SQL driver!\n");
6075 r = ERROR_FUNCTION_FAILED;
6078 uirow = MSI_CreateRecord( 5 );
6079 MSI_RecordSetStringW( uirow, 1, desc );
6080 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6081 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6082 msi_ui_actiondata( package, szInstallODBC, uirow );
6083 msiobj_release( &uirow->hdr );
6085 msi_free(driver);
6086 msi_free(driver_path);
6088 return r;
6091 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6093 MSIPACKAGE *package = param;
6094 LPWSTR translator, translator_path, ptr;
6095 WCHAR outpath[MAX_PATH];
6096 MSIFILE *translator_file = NULL, *setup_file = NULL;
6097 MSICOMPONENT *comp;
6098 MSIRECORD *uirow;
6099 LPCWSTR desc, file_key, component;
6100 DWORD len, usage;
6101 UINT r = ERROR_SUCCESS;
6103 static const WCHAR translator_fmt[] = {
6104 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6105 static const WCHAR setup_fmt[] = {
6106 'S','e','t','u','p','=','%','s',0};
6108 component = MSI_RecordGetString( rec, 2 );
6109 comp = msi_get_loaded_component( package, component );
6110 if (!comp)
6111 return ERROR_SUCCESS;
6113 comp->Action = msi_get_component_action( package, comp );
6114 if (comp->Action != INSTALLSTATE_LOCAL)
6116 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6117 return ERROR_SUCCESS;
6119 desc = MSI_RecordGetString(rec, 3);
6121 file_key = MSI_RecordGetString( rec, 4 );
6122 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6124 file_key = MSI_RecordGetString( rec, 5 );
6125 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6127 if (!translator_file)
6129 ERR("ODBC Translator entry not found!\n");
6130 return ERROR_FUNCTION_FAILED;
6133 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6134 if (setup_file)
6135 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6137 translator = msi_alloc(len * sizeof(WCHAR));
6138 if (!translator)
6139 return ERROR_OUTOFMEMORY;
6141 ptr = translator;
6142 lstrcpyW(ptr, desc);
6143 ptr += lstrlenW(ptr) + 1;
6145 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6146 ptr += len + 1;
6148 if (setup_file)
6150 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6151 ptr += len + 1;
6153 *ptr = '\0';
6155 translator_path = strdupW(translator_file->TargetPath);
6156 ptr = strrchrW(translator_path, '\\');
6157 if (ptr) *ptr = '\0';
6159 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6160 NULL, ODBC_INSTALL_COMPLETE, &usage))
6162 ERR("Failed to install SQL translator!\n");
6163 r = ERROR_FUNCTION_FAILED;
6166 uirow = MSI_CreateRecord( 5 );
6167 MSI_RecordSetStringW( uirow, 1, desc );
6168 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6169 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6170 msi_ui_actiondata( package, szInstallODBC, uirow );
6171 msiobj_release( &uirow->hdr );
6173 msi_free(translator);
6174 msi_free(translator_path);
6176 return r;
6179 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6181 MSIPACKAGE *package = param;
6182 MSICOMPONENT *comp;
6183 LPWSTR attrs;
6184 LPCWSTR desc, driver, component;
6185 WORD request = ODBC_ADD_SYS_DSN;
6186 INT registration;
6187 DWORD len;
6188 UINT r = ERROR_SUCCESS;
6189 MSIRECORD *uirow;
6191 static const WCHAR attrs_fmt[] = {
6192 'D','S','N','=','%','s',0 };
6194 component = MSI_RecordGetString( rec, 2 );
6195 comp = msi_get_loaded_component( package, component );
6196 if (!comp)
6197 return ERROR_SUCCESS;
6199 comp->Action = msi_get_component_action( package, comp );
6200 if (comp->Action != INSTALLSTATE_LOCAL)
6202 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6203 return ERROR_SUCCESS;
6206 desc = MSI_RecordGetString(rec, 3);
6207 driver = MSI_RecordGetString(rec, 4);
6208 registration = MSI_RecordGetInteger(rec, 5);
6210 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6211 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6213 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6214 attrs = msi_alloc(len * sizeof(WCHAR));
6215 if (!attrs)
6216 return ERROR_OUTOFMEMORY;
6218 len = sprintfW(attrs, attrs_fmt, desc);
6219 attrs[len + 1] = 0;
6221 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6223 ERR("Failed to install SQL data source!\n");
6224 r = ERROR_FUNCTION_FAILED;
6227 uirow = MSI_CreateRecord( 5 );
6228 MSI_RecordSetStringW( uirow, 1, desc );
6229 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6230 MSI_RecordSetInteger( uirow, 3, request );
6231 msi_ui_actiondata( package, szInstallODBC, uirow );
6232 msiobj_release( &uirow->hdr );
6234 msi_free(attrs);
6236 return r;
6239 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6241 static const WCHAR driver_query[] = {
6242 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6243 'O','D','B','C','D','r','i','v','e','r',0};
6244 static const WCHAR translator_query[] = {
6245 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6246 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6247 static const WCHAR source_query[] = {
6248 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6249 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6250 MSIQUERY *view;
6251 UINT rc;
6253 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6254 if (rc == ERROR_SUCCESS)
6256 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6257 msiobj_release(&view->hdr);
6258 if (rc != ERROR_SUCCESS)
6259 return rc;
6261 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6262 if (rc == ERROR_SUCCESS)
6264 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6265 msiobj_release(&view->hdr);
6266 if (rc != ERROR_SUCCESS)
6267 return rc;
6269 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6270 if (rc == ERROR_SUCCESS)
6272 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6273 msiobj_release(&view->hdr);
6274 if (rc != ERROR_SUCCESS)
6275 return rc;
6277 return ERROR_SUCCESS;
6280 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6282 MSIPACKAGE *package = param;
6283 MSICOMPONENT *comp;
6284 MSIRECORD *uirow;
6285 DWORD usage;
6286 LPCWSTR desc, component;
6288 component = MSI_RecordGetString( rec, 2 );
6289 comp = msi_get_loaded_component( package, component );
6290 if (!comp)
6291 return ERROR_SUCCESS;
6293 comp->Action = msi_get_component_action( package, comp );
6294 if (comp->Action != INSTALLSTATE_ABSENT)
6296 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6297 return ERROR_SUCCESS;
6300 desc = MSI_RecordGetString( rec, 3 );
6301 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6303 WARN("Failed to remove ODBC driver\n");
6305 else if (!usage)
6307 FIXME("Usage count reached 0\n");
6310 uirow = MSI_CreateRecord( 2 );
6311 MSI_RecordSetStringW( uirow, 1, desc );
6312 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6313 msi_ui_actiondata( package, szRemoveODBC, uirow );
6314 msiobj_release( &uirow->hdr );
6316 return ERROR_SUCCESS;
6319 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6321 MSIPACKAGE *package = param;
6322 MSICOMPONENT *comp;
6323 MSIRECORD *uirow;
6324 DWORD usage;
6325 LPCWSTR desc, component;
6327 component = MSI_RecordGetString( rec, 2 );
6328 comp = msi_get_loaded_component( package, component );
6329 if (!comp)
6330 return ERROR_SUCCESS;
6332 comp->Action = msi_get_component_action( package, comp );
6333 if (comp->Action != INSTALLSTATE_ABSENT)
6335 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6336 return ERROR_SUCCESS;
6339 desc = MSI_RecordGetString( rec, 3 );
6340 if (!SQLRemoveTranslatorW( desc, &usage ))
6342 WARN("Failed to remove ODBC translator\n");
6344 else if (!usage)
6346 FIXME("Usage count reached 0\n");
6349 uirow = MSI_CreateRecord( 2 );
6350 MSI_RecordSetStringW( uirow, 1, desc );
6351 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6352 msi_ui_actiondata( package, szRemoveODBC, uirow );
6353 msiobj_release( &uirow->hdr );
6355 return ERROR_SUCCESS;
6358 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6360 MSIPACKAGE *package = param;
6361 MSICOMPONENT *comp;
6362 MSIRECORD *uirow;
6363 LPWSTR attrs;
6364 LPCWSTR desc, driver, component;
6365 WORD request = ODBC_REMOVE_SYS_DSN;
6366 INT registration;
6367 DWORD len;
6369 static const WCHAR attrs_fmt[] = {
6370 'D','S','N','=','%','s',0 };
6372 component = MSI_RecordGetString( rec, 2 );
6373 comp = msi_get_loaded_component( package, component );
6374 if (!comp)
6375 return ERROR_SUCCESS;
6377 comp->Action = msi_get_component_action( package, comp );
6378 if (comp->Action != INSTALLSTATE_ABSENT)
6380 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6381 return ERROR_SUCCESS;
6384 desc = MSI_RecordGetString( rec, 3 );
6385 driver = MSI_RecordGetString( rec, 4 );
6386 registration = MSI_RecordGetInteger( rec, 5 );
6388 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6389 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6391 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6392 attrs = msi_alloc( len * sizeof(WCHAR) );
6393 if (!attrs)
6394 return ERROR_OUTOFMEMORY;
6396 FIXME("Use ODBCSourceAttribute table\n");
6398 len = sprintfW( attrs, attrs_fmt, desc );
6399 attrs[len + 1] = 0;
6401 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6403 WARN("Failed to remove ODBC data source\n");
6405 msi_free( attrs );
6407 uirow = MSI_CreateRecord( 3 );
6408 MSI_RecordSetStringW( uirow, 1, desc );
6409 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6410 MSI_RecordSetInteger( uirow, 3, request );
6411 msi_ui_actiondata( package, szRemoveODBC, uirow );
6412 msiobj_release( &uirow->hdr );
6414 return ERROR_SUCCESS;
6417 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6419 static const WCHAR driver_query[] = {
6420 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6421 'O','D','B','C','D','r','i','v','e','r',0};
6422 static const WCHAR translator_query[] = {
6423 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6424 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6425 static const WCHAR source_query[] = {
6426 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6427 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6428 MSIQUERY *view;
6429 UINT rc;
6431 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6432 if (rc == ERROR_SUCCESS)
6434 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6435 msiobj_release( &view->hdr );
6436 if (rc != ERROR_SUCCESS)
6437 return rc;
6439 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6440 if (rc == ERROR_SUCCESS)
6442 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6443 msiobj_release( &view->hdr );
6444 if (rc != ERROR_SUCCESS)
6445 return rc;
6447 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6448 if (rc == ERROR_SUCCESS)
6450 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6451 msiobj_release( &view->hdr );
6452 if (rc != ERROR_SUCCESS)
6453 return rc;
6455 return ERROR_SUCCESS;
6458 #define ENV_ACT_SETALWAYS 0x1
6459 #define ENV_ACT_SETABSENT 0x2
6460 #define ENV_ACT_REMOVE 0x4
6461 #define ENV_ACT_REMOVEMATCH 0x8
6463 #define ENV_MOD_MACHINE 0x20000000
6464 #define ENV_MOD_APPEND 0x40000000
6465 #define ENV_MOD_PREFIX 0x80000000
6466 #define ENV_MOD_MASK 0xC0000000
6468 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6470 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6472 LPCWSTR cptr = *name;
6474 static const WCHAR prefix[] = {'[','~',']',0};
6475 static const int prefix_len = 3;
6477 *flags = 0;
6478 while (*cptr)
6480 if (*cptr == '=')
6481 *flags |= ENV_ACT_SETALWAYS;
6482 else if (*cptr == '+')
6483 *flags |= ENV_ACT_SETABSENT;
6484 else if (*cptr == '-')
6485 *flags |= ENV_ACT_REMOVE;
6486 else if (*cptr == '!')
6487 *flags |= ENV_ACT_REMOVEMATCH;
6488 else if (*cptr == '*')
6489 *flags |= ENV_MOD_MACHINE;
6490 else
6491 break;
6493 cptr++;
6494 (*name)++;
6497 if (!*cptr)
6499 ERR("Missing environment variable\n");
6500 return ERROR_FUNCTION_FAILED;
6503 if (*value)
6505 LPCWSTR ptr = *value;
6506 if (!strncmpW(ptr, prefix, prefix_len))
6508 if (ptr[prefix_len] == szSemiColon[0])
6510 *flags |= ENV_MOD_APPEND;
6511 *value += lstrlenW(prefix);
6513 else
6515 *value = NULL;
6518 else if (lstrlenW(*value) >= prefix_len)
6520 ptr += lstrlenW(ptr) - prefix_len;
6521 if (!strcmpW( ptr, prefix ))
6523 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6525 *flags |= ENV_MOD_PREFIX;
6526 /* the "[~]" will be removed by deformat_string */;
6528 else
6530 *value = NULL;
6536 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6537 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6538 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6539 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6541 ERR("Invalid flags: %08x\n", *flags);
6542 return ERROR_FUNCTION_FAILED;
6545 if (!*flags)
6546 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6548 return ERROR_SUCCESS;
6551 static UINT open_env_key( DWORD flags, HKEY *key )
6553 static const WCHAR user_env[] =
6554 {'E','n','v','i','r','o','n','m','e','n','t',0};
6555 static const WCHAR machine_env[] =
6556 {'S','y','s','t','e','m','\\',
6557 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6558 'C','o','n','t','r','o','l','\\',
6559 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6560 'E','n','v','i','r','o','n','m','e','n','t',0};
6561 const WCHAR *env;
6562 HKEY root;
6563 LONG res;
6565 if (flags & ENV_MOD_MACHINE)
6567 env = machine_env;
6568 root = HKEY_LOCAL_MACHINE;
6570 else
6572 env = user_env;
6573 root = HKEY_CURRENT_USER;
6576 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6577 if (res != ERROR_SUCCESS)
6579 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6580 return ERROR_FUNCTION_FAILED;
6583 return ERROR_SUCCESS;
6586 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6588 MSIPACKAGE *package = param;
6589 LPCWSTR name, value, component;
6590 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6591 DWORD flags, type, size;
6592 UINT res;
6593 HKEY env = NULL;
6594 MSICOMPONENT *comp;
6595 MSIRECORD *uirow;
6596 int action = 0;
6598 component = MSI_RecordGetString(rec, 4);
6599 comp = msi_get_loaded_component(package, component);
6600 if (!comp)
6601 return ERROR_SUCCESS;
6603 comp->Action = msi_get_component_action( package, comp );
6604 if (comp->Action != INSTALLSTATE_LOCAL)
6606 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6607 return ERROR_SUCCESS;
6609 name = MSI_RecordGetString(rec, 2);
6610 value = MSI_RecordGetString(rec, 3);
6612 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6614 res = env_parse_flags(&name, &value, &flags);
6615 if (res != ERROR_SUCCESS || !value)
6616 goto done;
6618 if (value && !deformat_string(package, value, &deformatted))
6620 res = ERROR_OUTOFMEMORY;
6621 goto done;
6624 value = deformatted;
6626 res = open_env_key( flags, &env );
6627 if (res != ERROR_SUCCESS)
6628 goto done;
6630 if (flags & ENV_MOD_MACHINE)
6631 action |= 0x20000000;
6633 size = 0;
6634 type = REG_SZ;
6635 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6636 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6637 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6638 goto done;
6640 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6642 action = 0x2;
6644 /* Nothing to do. */
6645 if (!value)
6647 res = ERROR_SUCCESS;
6648 goto done;
6651 /* If we are appending but the string was empty, strip ; */
6652 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6654 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6655 newval = strdupW(value);
6656 if (!newval)
6658 res = ERROR_OUTOFMEMORY;
6659 goto done;
6662 else
6664 action = 0x1;
6666 /* Contrary to MSDN, +-variable to [~];path works */
6667 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6669 res = ERROR_SUCCESS;
6670 goto done;
6673 data = msi_alloc(size);
6674 if (!data)
6676 RegCloseKey(env);
6677 return ERROR_OUTOFMEMORY;
6680 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6681 if (res != ERROR_SUCCESS)
6682 goto done;
6684 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6686 action = 0x4;
6687 res = RegDeleteValueW(env, name);
6688 if (res != ERROR_SUCCESS)
6689 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6690 goto done;
6693 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6694 if (flags & ENV_MOD_MASK)
6696 DWORD mod_size;
6697 int multiplier = 0;
6698 if (flags & ENV_MOD_APPEND) multiplier++;
6699 if (flags & ENV_MOD_PREFIX) multiplier++;
6700 mod_size = lstrlenW(value) * multiplier;
6701 size += mod_size * sizeof(WCHAR);
6704 newval = msi_alloc(size);
6705 ptr = newval;
6706 if (!newval)
6708 res = ERROR_OUTOFMEMORY;
6709 goto done;
6712 if (flags & ENV_MOD_PREFIX)
6714 lstrcpyW(newval, value);
6715 ptr = newval + lstrlenW(value);
6716 action |= 0x80000000;
6719 lstrcpyW(ptr, data);
6721 if (flags & ENV_MOD_APPEND)
6723 lstrcatW(newval, value);
6724 action |= 0x40000000;
6727 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6728 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6729 if (res)
6731 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6734 done:
6735 uirow = MSI_CreateRecord( 3 );
6736 MSI_RecordSetStringW( uirow, 1, name );
6737 MSI_RecordSetStringW( uirow, 2, newval );
6738 MSI_RecordSetInteger( uirow, 3, action );
6739 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6740 msiobj_release( &uirow->hdr );
6742 if (env) RegCloseKey(env);
6743 msi_free(deformatted);
6744 msi_free(data);
6745 msi_free(newval);
6746 return res;
6749 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6751 static const WCHAR query[] = {
6752 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6753 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6754 MSIQUERY *view;
6755 UINT rc;
6757 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6758 if (rc != ERROR_SUCCESS)
6759 return ERROR_SUCCESS;
6761 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6762 msiobj_release(&view->hdr);
6763 return rc;
6766 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6768 MSIPACKAGE *package = param;
6769 LPCWSTR name, value, component;
6770 LPWSTR deformatted = NULL;
6771 DWORD flags;
6772 HKEY env;
6773 MSICOMPONENT *comp;
6774 MSIRECORD *uirow;
6775 int action = 0;
6776 LONG res;
6777 UINT r;
6779 component = MSI_RecordGetString( rec, 4 );
6780 comp = msi_get_loaded_component( package, component );
6781 if (!comp)
6782 return ERROR_SUCCESS;
6784 comp->Action = msi_get_component_action( package, comp );
6785 if (comp->Action != INSTALLSTATE_ABSENT)
6787 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6788 return ERROR_SUCCESS;
6790 name = MSI_RecordGetString( rec, 2 );
6791 value = MSI_RecordGetString( rec, 3 );
6793 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6795 r = env_parse_flags( &name, &value, &flags );
6796 if (r != ERROR_SUCCESS)
6797 return r;
6799 if (!(flags & ENV_ACT_REMOVE))
6801 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6802 return ERROR_SUCCESS;
6805 if (value && !deformat_string( package, value, &deformatted ))
6806 return ERROR_OUTOFMEMORY;
6808 value = deformatted;
6810 r = open_env_key( flags, &env );
6811 if (r != ERROR_SUCCESS)
6813 r = ERROR_SUCCESS;
6814 goto done;
6817 if (flags & ENV_MOD_MACHINE)
6818 action |= 0x20000000;
6820 TRACE("Removing %s\n", debugstr_w(name));
6822 res = RegDeleteValueW( env, name );
6823 if (res != ERROR_SUCCESS)
6825 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6826 r = ERROR_SUCCESS;
6829 done:
6830 uirow = MSI_CreateRecord( 3 );
6831 MSI_RecordSetStringW( uirow, 1, name );
6832 MSI_RecordSetStringW( uirow, 2, value );
6833 MSI_RecordSetInteger( uirow, 3, action );
6834 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6835 msiobj_release( &uirow->hdr );
6837 if (env) RegCloseKey( env );
6838 msi_free( deformatted );
6839 return r;
6842 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6844 static const WCHAR query[] = {
6845 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6846 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6847 MSIQUERY *view;
6848 UINT rc;
6850 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6851 if (rc != ERROR_SUCCESS)
6852 return ERROR_SUCCESS;
6854 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6855 msiobj_release( &view->hdr );
6856 return rc;
6859 UINT msi_validate_product_id( MSIPACKAGE *package )
6861 LPWSTR key, template, id;
6862 UINT r = ERROR_SUCCESS;
6864 id = msi_dup_property( package->db, szProductID );
6865 if (id)
6867 msi_free( id );
6868 return ERROR_SUCCESS;
6870 template = msi_dup_property( package->db, szPIDTemplate );
6871 key = msi_dup_property( package->db, szPIDKEY );
6872 if (key && template)
6874 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6875 r = msi_set_property( package->db, szProductID, key );
6877 msi_free( template );
6878 msi_free( key );
6879 return r;
6882 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6884 return msi_validate_product_id( package );
6887 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6889 TRACE("\n");
6890 package->need_reboot = 1;
6891 return ERROR_SUCCESS;
6894 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6896 static const WCHAR szAvailableFreeReg[] =
6897 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6898 MSIRECORD *uirow;
6899 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6901 TRACE("%p %d kilobytes\n", package, space);
6903 uirow = MSI_CreateRecord( 1 );
6904 MSI_RecordSetInteger( uirow, 1, space );
6905 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6906 msiobj_release( &uirow->hdr );
6908 return ERROR_SUCCESS;
6911 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6913 TRACE("%p\n", package);
6915 msi_set_property( package->db, szRollbackDisabled, szOne );
6916 return ERROR_SUCCESS;
6919 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6921 FIXME("%p\n", package);
6922 return ERROR_SUCCESS;
6925 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6927 static const WCHAR driver_query[] = {
6928 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6929 'O','D','B','C','D','r','i','v','e','r',0};
6930 static const WCHAR translator_query[] = {
6931 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6932 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6933 MSIQUERY *view;
6934 UINT r, count;
6936 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6937 if (r == ERROR_SUCCESS)
6939 count = 0;
6940 r = MSI_IterateRecords( view, &count, NULL, package );
6941 msiobj_release( &view->hdr );
6942 if (r != ERROR_SUCCESS)
6943 return r;
6944 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6946 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6947 if (r == ERROR_SUCCESS)
6949 count = 0;
6950 r = MSI_IterateRecords( view, &count, NULL, package );
6951 msiobj_release( &view->hdr );
6952 if (r != ERROR_SUCCESS)
6953 return r;
6954 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6956 return ERROR_SUCCESS;
6959 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6961 MSIPACKAGE *package = param;
6962 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6963 WCHAR *value;
6965 if ((value = msi_dup_property( package->db, property )))
6967 FIXME("remove %s\n", debugstr_w(value));
6968 msi_free( value );
6970 return ERROR_SUCCESS;
6973 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6975 static const WCHAR query[] = {
6976 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6977 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6978 MSIQUERY *view;
6979 UINT r;
6981 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6982 if (r == ERROR_SUCCESS)
6984 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6985 msiobj_release( &view->hdr );
6986 if (r != ERROR_SUCCESS)
6987 return r;
6989 return ERROR_SUCCESS;
6992 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6994 MSIPACKAGE *package = param;
6995 int attributes = MSI_RecordGetInteger( rec, 5 );
6997 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6999 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7000 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7001 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7002 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7003 HKEY hkey;
7004 UINT r;
7006 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7008 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7009 if (r != ERROR_SUCCESS)
7010 return ERROR_SUCCESS;
7012 else
7014 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7015 if (r != ERROR_SUCCESS)
7016 return ERROR_SUCCESS;
7018 RegCloseKey( hkey );
7020 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7021 debugstr_w(upgrade_code), debugstr_w(version_min),
7022 debugstr_w(version_max), debugstr_w(language));
7024 return ERROR_SUCCESS;
7027 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7029 static const WCHAR query[] = {
7030 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7031 'U','p','g','r','a','d','e',0};
7032 MSIQUERY *view;
7033 UINT r;
7035 if (msi_get_property_int( package->db, szInstalled, 0 ))
7037 TRACE("product is installed, skipping action\n");
7038 return ERROR_SUCCESS;
7040 if (msi_get_property_int( package->db, szPreselected, 0 ))
7042 TRACE("Preselected property is set, not migrating feature states\n");
7043 return ERROR_SUCCESS;
7045 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7046 if (r == ERROR_SUCCESS)
7048 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7049 msiobj_release( &view->hdr );
7050 if (r != ERROR_SUCCESS)
7051 return r;
7053 return ERROR_SUCCESS;
7056 static void bind_image( const char *filename, const char *path )
7058 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7060 WARN("failed to bind image %u\n", GetLastError());
7064 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7066 UINT i;
7067 MSIFILE *file;
7068 MSIPACKAGE *package = param;
7069 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7070 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7071 char *filenameA, *pathA;
7072 WCHAR *pathW, **path_list;
7074 if (!(file = msi_get_loaded_file( package, key )))
7076 WARN("file %s not found\n", debugstr_w(key));
7077 return ERROR_SUCCESS;
7079 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7080 path_list = msi_split_string( paths, ';' );
7081 if (!path_list) bind_image( filenameA, NULL );
7082 else
7084 for (i = 0; path_list[i] && path_list[i][0]; i++)
7086 deformat_string( package, path_list[i], &pathW );
7087 if ((pathA = strdupWtoA( pathW )))
7089 bind_image( filenameA, pathA );
7090 msi_free( pathA );
7092 msi_free( pathW );
7095 msi_free( path_list );
7096 msi_free( filenameA );
7097 return ERROR_SUCCESS;
7100 static UINT ACTION_BindImage( MSIPACKAGE *package )
7102 static const WCHAR query[] = {
7103 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7104 'B','i','n','d','I','m','a','g','e',0};
7105 MSIQUERY *view;
7106 UINT r;
7108 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7109 if (r == ERROR_SUCCESS)
7111 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7112 msiobj_release( &view->hdr );
7113 if (r != ERROR_SUCCESS)
7114 return r;
7116 return ERROR_SUCCESS;
7119 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7121 static const WCHAR query[] = {
7122 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7123 MSIQUERY *view;
7124 DWORD count = 0;
7125 UINT r;
7127 r = MSI_OpenQuery( package->db, &view, query, table );
7128 if (r == ERROR_SUCCESS)
7130 r = MSI_IterateRecords(view, &count, NULL, package);
7131 msiobj_release(&view->hdr);
7132 if (r != ERROR_SUCCESS)
7133 return r;
7135 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7136 return ERROR_SUCCESS;
7139 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7141 static const WCHAR table[] = {
7142 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7143 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7146 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7148 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7149 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7152 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7154 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7155 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7158 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7160 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7161 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7164 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7166 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7167 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7170 static const struct
7172 const WCHAR *action;
7173 UINT (*handler)(MSIPACKAGE *);
7174 const WCHAR *action_rollback;
7176 StandardActions[] =
7178 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7179 { szAppSearch, ACTION_AppSearch, NULL },
7180 { szBindImage, ACTION_BindImage, NULL },
7181 { szCCPSearch, ACTION_CCPSearch, NULL },
7182 { szCostFinalize, ACTION_CostFinalize, NULL },
7183 { szCostInitialize, ACTION_CostInitialize, NULL },
7184 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7185 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7186 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7187 { szDisableRollback, ACTION_DisableRollback, NULL },
7188 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7189 { szExecuteAction, ACTION_ExecuteAction, NULL },
7190 { szFileCost, ACTION_FileCost, NULL },
7191 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7192 { szForceReboot, ACTION_ForceReboot, NULL },
7193 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7194 { szInstallExecute, ACTION_InstallExecute, NULL },
7195 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7196 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7197 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7198 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7199 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7200 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7201 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7202 { szInstallValidate, ACTION_InstallValidate, NULL },
7203 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7204 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7205 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7206 { szMoveFiles, ACTION_MoveFiles, NULL },
7207 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7208 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7209 { szPatchFiles, ACTION_PatchFiles, NULL },
7210 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7211 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7212 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7213 { szPublishProduct, ACTION_PublishProduct, NULL },
7214 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7215 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7216 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7217 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7218 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7219 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7220 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7221 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7222 { szRegisterUser, ACTION_RegisterUser, NULL },
7223 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7224 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7225 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7226 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7227 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7228 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7229 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7230 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7231 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7232 { szResolveSource, ACTION_ResolveSource, NULL },
7233 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7234 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7235 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7236 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7237 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7238 { szStartServices, ACTION_StartServices, szStopServices },
7239 { szStopServices, ACTION_StopServices, szStartServices },
7240 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7241 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7242 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7243 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7244 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7245 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7246 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7247 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7248 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7249 { szValidateProductID, ACTION_ValidateProductID, NULL },
7250 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7251 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7252 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7253 { NULL, NULL, NULL }
7256 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7258 BOOL ret = FALSE;
7259 UINT i;
7261 i = 0;
7262 while (StandardActions[i].action != NULL)
7264 if (!strcmpW( StandardActions[i].action, action ))
7266 ui_actionstart( package, action );
7267 if (StandardActions[i].handler)
7269 ui_actioninfo( package, action, TRUE, 0 );
7270 *rc = StandardActions[i].handler( package );
7271 ui_actioninfo( package, action, FALSE, *rc );
7273 if (StandardActions[i].action_rollback && !package->need_rollback)
7275 TRACE("scheduling rollback action\n");
7276 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7279 else
7281 FIXME("unhandled standard action %s\n", debugstr_w(action));
7282 *rc = ERROR_SUCCESS;
7284 ret = TRUE;
7285 break;
7287 i++;
7289 return ret;
7292 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7294 UINT rc = ERROR_SUCCESS;
7295 BOOL handled;
7297 TRACE("Performing action (%s)\n", debugstr_w(action));
7299 handled = ACTION_HandleStandardAction(package, action, &rc);
7301 if (!handled)
7302 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7304 if (!handled)
7306 WARN("unhandled msi action %s\n", debugstr_w(action));
7307 rc = ERROR_FUNCTION_NOT_CALLED;
7310 return rc;
7313 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7315 UINT rc = ERROR_SUCCESS;
7316 BOOL handled = FALSE;
7318 TRACE("Performing action (%s)\n", debugstr_w(action));
7320 handled = ACTION_HandleStandardAction(package, action, &rc);
7322 if (!handled)
7323 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7325 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7326 handled = TRUE;
7328 if (!handled)
7330 WARN("unhandled msi action %s\n", debugstr_w(action));
7331 rc = ERROR_FUNCTION_NOT_CALLED;
7334 return rc;
7337 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7339 UINT rc = ERROR_SUCCESS;
7340 MSIRECORD *row;
7342 static const WCHAR query[] =
7343 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7344 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7345 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7346 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7347 static const WCHAR ui_query[] =
7348 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7349 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7350 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7351 ' ', '=',' ','%','i',0};
7353 if (needs_ui_sequence(package))
7354 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7355 else
7356 row = MSI_QueryGetRecord(package->db, query, seq);
7358 if (row)
7360 LPCWSTR action, cond;
7362 TRACE("Running the actions\n");
7364 /* check conditions */
7365 cond = MSI_RecordGetString(row, 2);
7367 /* this is a hack to skip errors in the condition code */
7368 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7370 msiobj_release(&row->hdr);
7371 return ERROR_SUCCESS;
7374 action = MSI_RecordGetString(row, 1);
7375 if (!action)
7377 ERR("failed to fetch action\n");
7378 msiobj_release(&row->hdr);
7379 return ERROR_FUNCTION_FAILED;
7382 if (needs_ui_sequence(package))
7383 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7384 else
7385 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7387 msiobj_release(&row->hdr);
7390 return rc;
7393 /****************************************************
7394 * TOP level entry points
7395 *****************************************************/
7397 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7398 LPCWSTR szCommandLine )
7400 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7401 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7402 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7403 WCHAR *reinstall = NULL;
7404 BOOL ui_exists;
7405 UINT rc;
7407 msi_set_property( package->db, szAction, szInstall );
7409 package->script->InWhatSequence = SEQUENCE_INSTALL;
7411 if (szPackagePath)
7413 LPWSTR p, dir;
7414 LPCWSTR file;
7416 dir = strdupW(szPackagePath);
7417 p = strrchrW(dir, '\\');
7418 if (p)
7420 *(++p) = 0;
7421 file = szPackagePath + (p - dir);
7423 else
7425 msi_free(dir);
7426 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7427 GetCurrentDirectoryW(MAX_PATH, dir);
7428 lstrcatW(dir, szBackSlash);
7429 file = szPackagePath;
7432 msi_free( package->PackagePath );
7433 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7434 if (!package->PackagePath)
7436 msi_free(dir);
7437 return ERROR_OUTOFMEMORY;
7440 lstrcpyW(package->PackagePath, dir);
7441 lstrcatW(package->PackagePath, file);
7442 msi_free(dir);
7444 msi_set_sourcedir_props(package, FALSE);
7447 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7448 if (rc != ERROR_SUCCESS)
7449 return rc;
7451 msi_apply_transforms( package );
7452 msi_apply_patches( package );
7454 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7456 TRACE("setting reinstall property\n");
7457 msi_set_property( package->db, szReinstall, szAll );
7460 /* properties may have been added by a transform */
7461 msi_clone_properties( package );
7463 msi_parse_command_line( package, szCommandLine, FALSE );
7464 msi_adjust_privilege_properties( package );
7465 msi_set_context( package );
7467 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7469 TRACE("disabling rollback\n");
7470 msi_set_property( package->db, szRollbackDisabled, szOne );
7473 if (needs_ui_sequence( package))
7475 package->script->InWhatSequence |= SEQUENCE_UI;
7476 rc = ACTION_ProcessUISequence(package);
7477 ui_exists = ui_sequence_exists(package);
7478 if (rc == ERROR_SUCCESS || !ui_exists)
7480 package->script->InWhatSequence |= SEQUENCE_EXEC;
7481 rc = ACTION_ProcessExecSequence(package, ui_exists);
7484 else
7485 rc = ACTION_ProcessExecSequence(package, FALSE);
7487 package->script->CurrentlyScripting = FALSE;
7489 /* process the ending type action */
7490 if (rc == ERROR_SUCCESS)
7491 ACTION_PerformActionSequence(package, -1);
7492 else if (rc == ERROR_INSTALL_USEREXIT)
7493 ACTION_PerformActionSequence(package, -2);
7494 else if (rc == ERROR_INSTALL_SUSPEND)
7495 ACTION_PerformActionSequence(package, -4);
7496 else /* failed */
7498 ACTION_PerformActionSequence(package, -3);
7499 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7501 package->need_rollback = TRUE;
7505 /* finish up running custom actions */
7506 ACTION_FinishCustomActions(package);
7508 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7510 WARN("installation failed, running rollback script\n");
7511 execute_script( package, SCRIPT_ROLLBACK );
7513 msi_free( reinstall );
7515 if (rc == ERROR_SUCCESS && package->need_reboot)
7516 return ERROR_SUCCESS_REBOOT_REQUIRED;
7518 return rc;