msi: Rename the need_reboot flag to need_reboot_at_end.
[wine.git] / dlls / msi / action.c
blob5ecf8a5dfd6bf152feda83a166a4dc661998b3b0
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 in_quotes = 1;
226 ignore = 1;
227 len++;
228 break;
229 case '"':
230 state = state_quote;
231 if (in_quotes && p[1] != '\"') count--;
232 else count++;
233 break;
234 default:
235 state = state_token;
236 in_quotes = 1;
237 len++;
238 break;
240 break;
242 case state_token:
243 switch (*p)
245 case '"':
246 state = state_quote;
247 if (in_quotes) count--;
248 else count++;
249 break;
250 case ' ':
251 state = state_whitespace;
252 if (!count) goto done;
253 in_quotes = 1;
254 len++;
255 break;
256 default:
257 if (!count) in_quotes = 0;
258 else in_quotes = 1;
259 len++;
260 break;
262 break;
264 case state_quote:
265 switch (*p)
267 case '"':
268 if (in_quotes && p[1] != '\"') count--;
269 else count++;
270 break;
271 case ' ':
272 state = state_whitespace;
273 if (!count || (count > 1 && !len)) goto done;
274 in_quotes = 1;
275 len++;
276 break;
277 default:
278 state = state_token;
279 if (!count) in_quotes = 0;
280 else in_quotes = 1;
281 len++;
282 break;
284 break;
286 default: break;
288 if (!ignore) *out++ = *p;
291 done:
292 if (!len) *value = 0;
293 else *out = 0;
295 *quotes = count;
296 return p - str;
299 static void remove_quotes( WCHAR *str )
301 WCHAR *p = str;
302 int len = strlenW( str );
304 while ((p = strchrW( p, '"' )))
306 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
307 p++;
311 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
312 BOOL preserve_case )
314 LPCWSTR ptr, ptr2;
315 int num_quotes;
316 DWORD len;
317 WCHAR *prop, *val;
318 UINT r;
320 if (!szCommandLine)
321 return ERROR_SUCCESS;
323 ptr = szCommandLine;
324 while (*ptr)
326 while (*ptr == ' ') ptr++;
327 if (!*ptr) break;
329 ptr2 = strchrW( ptr, '=' );
330 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
332 len = ptr2 - ptr;
333 if (!len) return ERROR_INVALID_COMMAND_LINE;
335 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
336 memcpy( prop, ptr, len * sizeof(WCHAR) );
337 prop[len] = 0;
338 if (!preserve_case) struprW( prop );
340 ptr2++;
341 while (*ptr2 == ' ') ptr2++;
343 num_quotes = 0;
344 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
345 len = parse_prop( ptr2, val, &num_quotes );
346 if (num_quotes % 2)
348 WARN("unbalanced quotes\n");
349 msi_free( val );
350 msi_free( prop );
351 return ERROR_INVALID_COMMAND_LINE;
353 remove_quotes( val );
354 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
356 r = msi_set_property( package->db, prop, val );
357 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
358 msi_reset_folders( package, TRUE );
360 msi_free( val );
361 msi_free( prop );
363 ptr = ptr2 + len;
366 return ERROR_SUCCESS;
369 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
371 LPCWSTR pc;
372 LPWSTR p, *ret = NULL;
373 UINT count = 0;
375 if (!str)
376 return ret;
378 /* count the number of substrings */
379 for ( pc = str, count = 0; pc; count++ )
381 pc = strchrW( pc, sep );
382 if (pc)
383 pc++;
386 /* allocate space for an array of substring pointers and the substrings */
387 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
388 (lstrlenW(str)+1) * sizeof(WCHAR) );
389 if (!ret)
390 return ret;
392 /* copy the string and set the pointers */
393 p = (LPWSTR) &ret[count+1];
394 lstrcpyW( p, str );
395 for( count = 0; (ret[count] = p); count++ )
397 p = strchrW( p, sep );
398 if (p)
399 *p++ = 0;
402 return ret;
405 static BOOL ui_sequence_exists( MSIPACKAGE *package )
407 static const WCHAR query [] = {
408 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
409 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
410 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
411 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
412 MSIQUERY *view;
413 UINT rc;
415 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
416 if (rc == ERROR_SUCCESS)
418 msiobj_release(&view->hdr);
419 return TRUE;
421 return FALSE;
424 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
426 LPWSTR source, check;
428 if (msi_get_property_int( package->db, szInstalled, 0 ))
430 HKEY hkey;
432 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
433 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
434 RegCloseKey( hkey );
436 else
438 LPWSTR p, db;
439 DWORD len;
441 db = msi_dup_property( package->db, szOriginalDatabase );
442 if (!db)
443 return ERROR_OUTOFMEMORY;
445 p = strrchrW( db, '\\' );
446 if (!p)
448 p = strrchrW( db, '/' );
449 if (!p)
451 msi_free(db);
452 return ERROR_SUCCESS;
456 len = p - db + 2;
457 source = msi_alloc( len * sizeof(WCHAR) );
458 lstrcpynW( source, db, len );
459 msi_free( db );
462 check = msi_dup_property( package->db, szSourceDir );
463 if (!check || replace)
465 UINT r = msi_set_property( package->db, szSourceDir, source );
466 if (r == ERROR_SUCCESS)
467 msi_reset_folders( package, TRUE );
469 msi_free( check );
471 check = msi_dup_property( package->db, szSOURCEDIR );
472 if (!check || replace)
473 msi_set_property( package->db, szSOURCEDIR, source );
475 msi_free( check );
476 msi_free( source );
478 return ERROR_SUCCESS;
481 static BOOL needs_ui_sequence(MSIPACKAGE *package)
483 return (gUILevel & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
486 UINT msi_set_context(MSIPACKAGE *package)
488 UINT r = msi_locate_product( package->ProductCode, &package->Context );
489 if (r != ERROR_SUCCESS)
491 int num = msi_get_property_int( package->db, szAllUsers, 0 );
492 if (num == 1 || num == 2)
493 package->Context = MSIINSTALLCONTEXT_MACHINE;
494 else
495 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
497 return ERROR_SUCCESS;
500 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
502 UINT rc;
503 LPCWSTR cond, action;
504 MSIPACKAGE *package = param;
506 action = MSI_RecordGetString(row,1);
507 if (!action)
509 ERR("Error is retrieving action name\n");
510 return ERROR_FUNCTION_FAILED;
513 /* check conditions */
514 cond = MSI_RecordGetString(row,2);
516 /* this is a hack to skip errors in the condition code */
517 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
519 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
520 return ERROR_SUCCESS;
523 if (needs_ui_sequence(package))
524 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
525 else
526 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
528 msi_dialog_check_messages( NULL );
530 if (package->CurrentInstallState != ERROR_SUCCESS)
531 rc = package->CurrentInstallState;
533 if (rc == ERROR_FUNCTION_NOT_CALLED)
534 rc = ERROR_SUCCESS;
536 if (rc != ERROR_SUCCESS)
537 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
539 return rc;
542 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
544 static const WCHAR query[] = {
545 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
546 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
547 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
548 '`','S','e','q','u','e','n','c','e','`',0};
549 MSIQUERY *view;
550 UINT r;
552 TRACE("%p %s\n", package, debugstr_w(table));
554 r = MSI_OpenQuery( package->db, &view, query, table );
555 if (r == ERROR_SUCCESS)
557 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
558 msiobj_release(&view->hdr);
560 return r;
563 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
565 static const WCHAR query[] = {
566 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
567 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
568 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
569 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
570 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
571 static const WCHAR query_validate[] = {
572 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
573 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
574 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
575 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
576 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
577 MSIQUERY *view;
578 INT seq = 0;
579 UINT rc;
581 if (package->script->ExecuteSequenceRun)
583 TRACE("Execute Sequence already Run\n");
584 return ERROR_SUCCESS;
587 package->script->ExecuteSequenceRun = TRUE;
589 /* get the sequence number */
590 if (UIran)
592 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
593 if (!row) return ERROR_FUNCTION_FAILED;
594 seq = MSI_RecordGetInteger(row,1);
595 msiobj_release(&row->hdr);
597 rc = MSI_OpenQuery(package->db, &view, query, seq);
598 if (rc == ERROR_SUCCESS)
600 TRACE("Running the actions\n");
602 msi_set_property(package->db, szSourceDir, NULL);
603 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
604 msiobj_release(&view->hdr);
606 return rc;
609 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
611 static const WCHAR query[] = {
612 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
613 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
614 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
615 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
616 MSIQUERY *view;
617 UINT rc;
619 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
620 if (rc == ERROR_SUCCESS)
622 TRACE("Running the actions\n");
623 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
624 msiobj_release(&view->hdr);
626 return rc;
629 /********************************************************
630 * ACTION helper functions and functions that perform the actions
631 *******************************************************/
632 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
633 UINT* rc, UINT script, BOOL force )
635 BOOL ret=FALSE;
636 UINT arc;
638 arc = ACTION_CustomAction(package, action, script, force);
640 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
642 *rc = arc;
643 ret = TRUE;
645 return ret;
648 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
650 MSICOMPONENT *comp;
652 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
654 if (!strcmpW( Component, comp->Component )) return comp;
656 return NULL;
659 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
661 MSIFEATURE *feature;
663 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
665 if (!strcmpW( Feature, feature->Feature )) return feature;
667 return NULL;
670 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
672 MSIFILE *file;
674 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
676 if (!strcmpW( key, file->File )) return file;
678 return NULL;
681 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
683 MSIFILEPATCH *patch;
685 /* FIXME: There might be more than one patch */
686 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
688 if (!strcmpW( key, patch->File->File )) return patch;
690 return NULL;
693 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
695 MSIFOLDER *folder;
697 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
699 if (!strcmpW( dir, folder->Directory )) return folder;
701 return NULL;
705 * Recursively create all directories in the path.
706 * shamelessly stolen from setupapi/queue.c
708 BOOL msi_create_full_path( const WCHAR *path )
710 BOOL ret = TRUE;
711 WCHAR *new_path;
712 int len;
714 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
715 strcpyW( new_path, path );
717 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
718 new_path[len - 1] = 0;
720 while (!CreateDirectoryW( new_path, NULL ))
722 WCHAR *slash;
723 DWORD last_error = GetLastError();
724 if (last_error == ERROR_ALREADY_EXISTS) break;
725 if (last_error != ERROR_PATH_NOT_FOUND)
727 ret = FALSE;
728 break;
730 if (!(slash = strrchrW( new_path, '\\' )))
732 ret = FALSE;
733 break;
735 len = slash - new_path;
736 new_path[len] = 0;
737 if (!msi_create_full_path( new_path ))
739 ret = FALSE;
740 break;
742 new_path[len] = '\\';
744 msi_free( new_path );
745 return ret;
748 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
750 MSIRECORD *row;
752 row = MSI_CreateRecord( 4 );
753 MSI_RecordSetInteger( row, 1, a );
754 MSI_RecordSetInteger( row, 2, b );
755 MSI_RecordSetInteger( row, 3, c );
756 MSI_RecordSetInteger( row, 4, d );
757 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
758 msiobj_release( &row->hdr );
760 msi_dialog_check_messages( NULL );
763 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
765 static const WCHAR query[] =
766 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
767 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
768 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
769 WCHAR message[1024];
770 MSIRECORD *row = 0;
771 DWORD size;
773 if (!package->LastAction || strcmpW( package->LastAction, action ))
775 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
777 if (MSI_RecordIsNull( row, 3 ))
779 msiobj_release( &row->hdr );
780 return;
782 /* update the cached action format */
783 msi_free( package->ActionFormat );
784 package->ActionFormat = msi_dup_record_field( row, 3 );
785 msi_free( package->LastAction );
786 package->LastAction = strdupW( action );
787 msiobj_release( &row->hdr );
789 size = 1024;
790 MSI_RecordSetStringW( record, 0, package->ActionFormat );
791 MSI_FormatRecordW( package, record, message, &size );
792 row = MSI_CreateRecord( 1 );
793 MSI_RecordSetStringW( row, 1, message );
794 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
795 msiobj_release( &row->hdr );
798 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
800 if (!comp->Enabled)
802 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
803 return INSTALLSTATE_UNKNOWN;
805 if (package->need_rollback) return comp->Installed;
806 return comp->ActionRequest;
809 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
811 if (package->need_rollback) return feature->Installed;
812 return feature->ActionRequest;
815 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
817 MSIPACKAGE *package = param;
818 LPCWSTR dir, component, full_path;
819 MSIRECORD *uirow;
820 MSIFOLDER *folder;
821 MSICOMPONENT *comp;
823 component = MSI_RecordGetString(row, 2);
824 if (!component)
825 return ERROR_SUCCESS;
827 comp = msi_get_loaded_component(package, component);
828 if (!comp)
829 return ERROR_SUCCESS;
831 comp->Action = msi_get_component_action( package, comp );
832 if (comp->Action != INSTALLSTATE_LOCAL)
834 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
835 return ERROR_SUCCESS;
838 dir = MSI_RecordGetString(row,1);
839 if (!dir)
841 ERR("Unable to get folder id\n");
842 return ERROR_SUCCESS;
845 uirow = MSI_CreateRecord(1);
846 MSI_RecordSetStringW(uirow, 1, dir);
847 msi_ui_actiondata(package, szCreateFolders, uirow);
848 msiobj_release(&uirow->hdr);
850 full_path = msi_get_target_folder( package, dir );
851 if (!full_path)
853 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
854 return ERROR_SUCCESS;
856 TRACE("folder is %s\n", debugstr_w(full_path));
858 folder = msi_get_loaded_folder( package, dir );
859 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
860 folder->State = FOLDER_STATE_CREATED;
861 return ERROR_SUCCESS;
864 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
866 static const WCHAR query[] = {
867 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
868 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
869 MSIQUERY *view;
870 UINT rc;
872 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
873 if (rc != ERROR_SUCCESS)
874 return ERROR_SUCCESS;
876 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
877 msiobj_release(&view->hdr);
878 return rc;
881 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
883 MSIPACKAGE *package = param;
884 LPCWSTR dir, component, full_path;
885 MSIRECORD *uirow;
886 MSIFOLDER *folder;
887 MSICOMPONENT *comp;
889 component = MSI_RecordGetString(row, 2);
890 if (!component)
891 return ERROR_SUCCESS;
893 comp = msi_get_loaded_component(package, component);
894 if (!comp)
895 return ERROR_SUCCESS;
897 comp->Action = msi_get_component_action( package, comp );
898 if (comp->Action != INSTALLSTATE_ABSENT)
900 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
901 return ERROR_SUCCESS;
904 dir = MSI_RecordGetString( row, 1 );
905 if (!dir)
907 ERR("Unable to get folder id\n");
908 return ERROR_SUCCESS;
911 full_path = msi_get_target_folder( package, dir );
912 if (!full_path)
914 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
915 return ERROR_SUCCESS;
917 TRACE("folder is %s\n", debugstr_w(full_path));
919 uirow = MSI_CreateRecord( 1 );
920 MSI_RecordSetStringW( uirow, 1, dir );
921 msi_ui_actiondata( package, szRemoveFolders, uirow );
922 msiobj_release( &uirow->hdr );
924 RemoveDirectoryW( full_path );
925 folder = msi_get_loaded_folder( package, dir );
926 folder->State = FOLDER_STATE_REMOVED;
927 return ERROR_SUCCESS;
930 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
932 static const WCHAR query[] = {
933 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
934 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
935 MSIQUERY *view;
936 UINT rc;
938 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
939 if (rc != ERROR_SUCCESS)
940 return ERROR_SUCCESS;
942 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
943 msiobj_release( &view->hdr );
944 return rc;
947 static UINT load_component( MSIRECORD *row, LPVOID param )
949 MSIPACKAGE *package = param;
950 MSICOMPONENT *comp;
952 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
953 if (!comp)
954 return ERROR_FUNCTION_FAILED;
956 list_add_tail( &package->components, &comp->entry );
958 /* fill in the data */
959 comp->Component = msi_dup_record_field( row, 1 );
961 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
963 comp->ComponentId = msi_dup_record_field( row, 2 );
964 comp->Directory = msi_dup_record_field( row, 3 );
965 comp->Attributes = MSI_RecordGetInteger(row,4);
966 comp->Condition = msi_dup_record_field( row, 5 );
967 comp->KeyPath = msi_dup_record_field( row, 6 );
969 comp->Installed = INSTALLSTATE_UNKNOWN;
970 comp->Action = INSTALLSTATE_UNKNOWN;
971 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
973 comp->assembly = msi_load_assembly( package, comp );
974 return ERROR_SUCCESS;
977 UINT msi_load_all_components( MSIPACKAGE *package )
979 static const WCHAR query[] = {
980 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
981 '`','C','o','m','p','o','n','e','n','t','`',0};
982 MSIQUERY *view;
983 UINT r;
985 if (!list_empty(&package->components))
986 return ERROR_SUCCESS;
988 r = MSI_DatabaseOpenViewW( package->db, query, &view );
989 if (r != ERROR_SUCCESS)
990 return r;
992 if (!msi_init_assembly_caches( package ))
994 ERR("can't initialize assembly caches\n");
995 msiobj_release( &view->hdr );
996 return ERROR_FUNCTION_FAILED;
999 r = MSI_IterateRecords(view, NULL, load_component, package);
1000 msiobj_release(&view->hdr);
1001 msi_destroy_assembly_caches( package );
1002 return r;
1005 typedef struct {
1006 MSIPACKAGE *package;
1007 MSIFEATURE *feature;
1008 } _ilfs;
1010 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1012 ComponentList *cl;
1014 cl = msi_alloc( sizeof (*cl) );
1015 if ( !cl )
1016 return ERROR_NOT_ENOUGH_MEMORY;
1017 cl->component = comp;
1018 list_add_tail( &feature->Components, &cl->entry );
1020 return ERROR_SUCCESS;
1023 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1025 FeatureList *fl;
1027 fl = msi_alloc( sizeof(*fl) );
1028 if ( !fl )
1029 return ERROR_NOT_ENOUGH_MEMORY;
1030 fl->feature = child;
1031 list_add_tail( &parent->Children, &fl->entry );
1033 return ERROR_SUCCESS;
1036 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1038 _ilfs* ilfs = param;
1039 LPCWSTR component;
1040 MSICOMPONENT *comp;
1042 component = MSI_RecordGetString(row,1);
1044 /* check to see if the component is already loaded */
1045 comp = msi_get_loaded_component( ilfs->package, component );
1046 if (!comp)
1048 WARN("ignoring unknown component %s\n", debugstr_w(component));
1049 return ERROR_SUCCESS;
1051 add_feature_component( ilfs->feature, comp );
1052 comp->Enabled = TRUE;
1054 return ERROR_SUCCESS;
1057 static UINT load_feature(MSIRECORD * row, LPVOID param)
1059 static const WCHAR query[] = {
1060 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1061 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1062 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1063 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1064 MSIPACKAGE *package = param;
1065 MSIFEATURE *feature;
1066 MSIQUERY *view;
1067 _ilfs ilfs;
1068 UINT rc;
1070 /* fill in the data */
1072 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1073 if (!feature)
1074 return ERROR_NOT_ENOUGH_MEMORY;
1076 list_init( &feature->Children );
1077 list_init( &feature->Components );
1079 feature->Feature = msi_dup_record_field( row, 1 );
1081 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1083 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1084 feature->Title = msi_dup_record_field( row, 3 );
1085 feature->Description = msi_dup_record_field( row, 4 );
1087 if (!MSI_RecordIsNull(row,5))
1088 feature->Display = MSI_RecordGetInteger(row,5);
1090 feature->Level= MSI_RecordGetInteger(row,6);
1091 feature->Directory = msi_dup_record_field( row, 7 );
1092 feature->Attributes = MSI_RecordGetInteger(row,8);
1094 feature->Installed = INSTALLSTATE_UNKNOWN;
1095 feature->Action = INSTALLSTATE_UNKNOWN;
1096 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1098 list_add_tail( &package->features, &feature->entry );
1100 /* load feature components */
1102 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1103 if (rc != ERROR_SUCCESS)
1104 return ERROR_SUCCESS;
1106 ilfs.package = package;
1107 ilfs.feature = feature;
1109 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1110 msiobj_release(&view->hdr);
1111 return rc;
1114 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1116 MSIPACKAGE *package = param;
1117 MSIFEATURE *parent, *child;
1119 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1120 if (!child)
1121 return ERROR_FUNCTION_FAILED;
1123 if (!child->Feature_Parent)
1124 return ERROR_SUCCESS;
1126 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1127 if (!parent)
1128 return ERROR_FUNCTION_FAILED;
1130 add_feature_child( parent, child );
1131 return ERROR_SUCCESS;
1134 UINT msi_load_all_features( MSIPACKAGE *package )
1136 static const WCHAR query[] = {
1137 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1138 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1139 '`','D','i','s','p','l','a','y','`',0};
1140 MSIQUERY *view;
1141 UINT r;
1143 if (!list_empty(&package->features))
1144 return ERROR_SUCCESS;
1146 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1147 if (r != ERROR_SUCCESS)
1148 return r;
1150 r = MSI_IterateRecords( view, NULL, load_feature, package );
1151 if (r != ERROR_SUCCESS)
1153 msiobj_release( &view->hdr );
1154 return r;
1156 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1157 msiobj_release( &view->hdr );
1158 return r;
1161 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1163 if (!p)
1164 return p;
1165 p = strchrW(p, ch);
1166 if (!p)
1167 return p;
1168 *p = 0;
1169 return p+1;
1172 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1174 static const WCHAR query[] = {
1175 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1176 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1177 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1178 MSIQUERY *view = NULL;
1179 MSIRECORD *row = NULL;
1180 UINT r;
1182 TRACE("%s\n", debugstr_w(file->File));
1184 r = MSI_OpenQuery(package->db, &view, query, file->File);
1185 if (r != ERROR_SUCCESS)
1186 goto done;
1188 r = MSI_ViewExecute(view, NULL);
1189 if (r != ERROR_SUCCESS)
1190 goto done;
1192 r = MSI_ViewFetch(view, &row);
1193 if (r != ERROR_SUCCESS)
1194 goto done;
1196 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1197 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1198 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1199 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1200 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1202 done:
1203 if (view) msiobj_release(&view->hdr);
1204 if (row) msiobj_release(&row->hdr);
1205 return r;
1208 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1210 MSIRECORD *row;
1211 static const WCHAR query[] = {
1212 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1213 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1214 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1216 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1217 if (!row)
1219 WARN("query failed\n");
1220 return ERROR_FUNCTION_FAILED;
1223 file->disk_id = MSI_RecordGetInteger( row, 1 );
1224 msiobj_release( &row->hdr );
1225 return ERROR_SUCCESS;
1228 static UINT load_file(MSIRECORD *row, LPVOID param)
1230 MSIPACKAGE* package = param;
1231 LPCWSTR component;
1232 MSIFILE *file;
1234 /* fill in the data */
1236 file = msi_alloc_zero( sizeof (MSIFILE) );
1237 if (!file)
1238 return ERROR_NOT_ENOUGH_MEMORY;
1240 file->File = msi_dup_record_field( row, 1 );
1242 component = MSI_RecordGetString( row, 2 );
1243 file->Component = msi_get_loaded_component( package, component );
1245 if (!file->Component)
1247 WARN("Component not found: %s\n", debugstr_w(component));
1248 msi_free(file->File);
1249 msi_free(file);
1250 return ERROR_SUCCESS;
1253 file->FileName = msi_dup_record_field( row, 3 );
1254 msi_reduce_to_long_filename( file->FileName );
1256 file->ShortName = msi_dup_record_field( row, 3 );
1257 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1259 file->FileSize = MSI_RecordGetInteger( row, 4 );
1260 file->Version = msi_dup_record_field( row, 5 );
1261 file->Language = msi_dup_record_field( row, 6 );
1262 file->Attributes = MSI_RecordGetInteger( row, 7 );
1263 file->Sequence = MSI_RecordGetInteger( row, 8 );
1265 file->state = msifs_invalid;
1267 /* if the compressed bits are not set in the file attributes,
1268 * then read the information from the package word count property
1270 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1272 file->IsCompressed = FALSE;
1274 else if (file->Attributes &
1275 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1277 file->IsCompressed = TRUE;
1279 else if (file->Attributes & msidbFileAttributesNoncompressed)
1281 file->IsCompressed = FALSE;
1283 else
1285 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1288 load_file_hash(package, file);
1289 load_file_disk_id(package, file);
1291 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1293 list_add_tail( &package->files, &file->entry );
1295 return ERROR_SUCCESS;
1298 static UINT load_all_files(MSIPACKAGE *package)
1300 static const WCHAR query[] = {
1301 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1302 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1303 '`','S','e','q','u','e','n','c','e','`', 0};
1304 MSIQUERY *view;
1305 UINT rc;
1307 if (!list_empty(&package->files))
1308 return ERROR_SUCCESS;
1310 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1311 if (rc != ERROR_SUCCESS)
1312 return ERROR_SUCCESS;
1314 rc = MSI_IterateRecords(view, NULL, load_file, package);
1315 msiobj_release(&view->hdr);
1316 return rc;
1319 static UINT load_media( MSIRECORD *row, LPVOID param )
1321 MSIPACKAGE *package = param;
1322 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1323 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1325 /* FIXME: load external cabinets and directory sources too */
1326 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1327 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1328 return ERROR_SUCCESS;
1331 static UINT load_all_media( MSIPACKAGE *package )
1333 static const WCHAR query[] = {
1334 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1335 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1336 '`','D','i','s','k','I','d','`',0};
1337 MSIQUERY *view;
1338 UINT r;
1340 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1341 if (r != ERROR_SUCCESS)
1342 return ERROR_SUCCESS;
1344 r = MSI_IterateRecords( view, NULL, load_media, package );
1345 msiobj_release( &view->hdr );
1346 return r;
1349 static UINT load_patch(MSIRECORD *row, LPVOID param)
1351 MSIPACKAGE *package = param;
1352 MSIFILEPATCH *patch;
1353 LPWSTR file_key;
1355 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1356 if (!patch)
1357 return ERROR_NOT_ENOUGH_MEMORY;
1359 file_key = msi_dup_record_field( row, 1 );
1360 patch->File = msi_get_loaded_file( package, file_key );
1361 msi_free(file_key);
1363 if( !patch->File )
1365 ERR("Failed to find target for patch in File table\n");
1366 msi_free(patch);
1367 return ERROR_FUNCTION_FAILED;
1370 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1372 /* FIXME: The database should be properly transformed */
1373 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1375 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1376 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1377 patch->IsApplied = FALSE;
1379 /* FIXME:
1380 * Header field - for patch validation.
1381 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1384 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1386 list_add_tail( &package->filepatches, &patch->entry );
1388 return ERROR_SUCCESS;
1391 static UINT load_all_patches(MSIPACKAGE *package)
1393 static const WCHAR query[] = {
1394 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1395 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1396 '`','S','e','q','u','e','n','c','e','`',0};
1397 MSIQUERY *view;
1398 UINT rc;
1400 if (!list_empty(&package->filepatches))
1401 return ERROR_SUCCESS;
1403 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1404 if (rc != ERROR_SUCCESS)
1405 return ERROR_SUCCESS;
1407 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1408 msiobj_release(&view->hdr);
1409 return rc;
1412 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1414 static const WCHAR query[] = {
1415 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1416 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1417 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1418 MSIQUERY *view;
1420 folder->persistent = FALSE;
1421 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1423 if (!MSI_ViewExecute( view, NULL ))
1425 MSIRECORD *rec;
1426 if (!MSI_ViewFetch( view, &rec ))
1428 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1429 folder->persistent = TRUE;
1430 msiobj_release( &rec->hdr );
1433 msiobj_release( &view->hdr );
1435 return ERROR_SUCCESS;
1438 static UINT load_folder( MSIRECORD *row, LPVOID param )
1440 MSIPACKAGE *package = param;
1441 static WCHAR szEmpty[] = { 0 };
1442 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1443 MSIFOLDER *folder;
1445 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1446 list_init( &folder->children );
1447 folder->Directory = msi_dup_record_field( row, 1 );
1448 folder->Parent = msi_dup_record_field( row, 2 );
1449 p = msi_dup_record_field(row, 3);
1451 TRACE("%s\n", debugstr_w(folder->Directory));
1453 /* split src and target dir */
1454 tgt_short = p;
1455 src_short = folder_split_path( p, ':' );
1457 /* split the long and short paths */
1458 tgt_long = folder_split_path( tgt_short, '|' );
1459 src_long = folder_split_path( src_short, '|' );
1461 /* check for no-op dirs */
1462 if (tgt_short && !strcmpW( szDot, tgt_short ))
1463 tgt_short = szEmpty;
1464 if (src_short && !strcmpW( szDot, src_short ))
1465 src_short = szEmpty;
1467 if (!tgt_long)
1468 tgt_long = tgt_short;
1470 if (!src_short) {
1471 src_short = tgt_short;
1472 src_long = tgt_long;
1475 if (!src_long)
1476 src_long = src_short;
1478 /* FIXME: use the target short path too */
1479 folder->TargetDefault = strdupW(tgt_long);
1480 folder->SourceShortPath = strdupW(src_short);
1481 folder->SourceLongPath = strdupW(src_long);
1482 msi_free(p);
1484 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1485 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1486 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1488 load_folder_persistence( package, folder );
1490 list_add_tail( &package->folders, &folder->entry );
1491 return ERROR_SUCCESS;
1494 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1496 FolderList *fl;
1498 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1499 fl->folder = child;
1500 list_add_tail( &parent->children, &fl->entry );
1501 return ERROR_SUCCESS;
1504 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1506 MSIPACKAGE *package = param;
1507 MSIFOLDER *parent, *child;
1509 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1510 return ERROR_FUNCTION_FAILED;
1512 if (!child->Parent) return ERROR_SUCCESS;
1514 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1515 return ERROR_FUNCTION_FAILED;
1517 return add_folder_child( parent, child );
1520 static UINT load_all_folders( MSIPACKAGE *package )
1522 static const WCHAR query[] = {
1523 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1524 '`','D','i','r','e','c','t','o','r','y','`',0};
1525 MSIQUERY *view;
1526 UINT r;
1528 if (!list_empty(&package->folders))
1529 return ERROR_SUCCESS;
1531 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1532 if (r != ERROR_SUCCESS)
1533 return r;
1535 r = MSI_IterateRecords( view, NULL, load_folder, package );
1536 if (r != ERROR_SUCCESS)
1538 msiobj_release( &view->hdr );
1539 return r;
1541 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1542 msiobj_release( &view->hdr );
1543 return r;
1546 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1548 msi_set_property( package->db, szCostingComplete, szZero );
1549 msi_set_property( package->db, szRootDrive, szCRoot );
1551 load_all_folders( package );
1552 msi_load_all_components( package );
1553 msi_load_all_features( package );
1554 load_all_files( package );
1555 load_all_patches( package );
1556 load_all_media( package );
1558 return ERROR_SUCCESS;
1561 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1563 const WCHAR *action = package->script->Actions[script][index];
1564 ui_actionstart( package, action );
1565 TRACE("executing %s\n", debugstr_w(action));
1566 return ACTION_PerformAction( package, action, script );
1569 static UINT execute_script( MSIPACKAGE *package, UINT script )
1571 UINT i, rc = ERROR_SUCCESS;
1573 TRACE("executing script %u\n", script);
1575 if (!package->script)
1577 ERR("no script!\n");
1578 return ERROR_FUNCTION_FAILED;
1580 if (script == SCRIPT_ROLLBACK)
1582 for (i = package->script->ActionCount[script]; i > 0; i--)
1584 rc = execute_script_action( package, script, i - 1 );
1585 if (rc != ERROR_SUCCESS) break;
1588 else
1590 for (i = 0; i < package->script->ActionCount[script]; i++)
1592 rc = execute_script_action( package, script, i );
1593 if (rc != ERROR_SUCCESS) break;
1596 msi_free_action_script(package, script);
1597 return rc;
1600 static UINT ACTION_FileCost(MSIPACKAGE *package)
1602 return ERROR_SUCCESS;
1605 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1607 MSICOMPONENT *comp;
1608 UINT r;
1610 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1612 if (!comp->ComponentId) continue;
1614 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1615 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1616 &comp->Installed );
1617 if (r != ERROR_SUCCESS)
1618 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1619 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1620 &comp->Installed );
1621 if (r != ERROR_SUCCESS)
1622 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1623 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1624 &comp->Installed );
1625 if (r != ERROR_SUCCESS)
1626 comp->Installed = INSTALLSTATE_ABSENT;
1630 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1632 MSIFEATURE *feature;
1634 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1636 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1638 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1639 feature->Installed = INSTALLSTATE_ABSENT;
1640 else
1641 feature->Installed = state;
1645 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1647 return (feature->Level > 0 && feature->Level <= level);
1650 static BOOL process_state_property(MSIPACKAGE* package, int level,
1651 LPCWSTR property, INSTALLSTATE state)
1653 LPWSTR override;
1654 MSIFEATURE *feature;
1656 override = msi_dup_property( package->db, property );
1657 if (!override)
1658 return FALSE;
1660 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1662 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1663 continue;
1665 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1667 if (!strcmpiW( override, szAll ))
1669 if (feature->Installed != state)
1671 feature->Action = state;
1672 feature->ActionRequest = state;
1675 else
1677 LPWSTR ptr = override;
1678 LPWSTR ptr2 = strchrW(override,',');
1680 while (ptr)
1682 int len = ptr2 - ptr;
1684 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1685 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1687 if (feature->Installed != state)
1689 feature->Action = state;
1690 feature->ActionRequest = state;
1692 break;
1694 if (ptr2)
1696 ptr=ptr2+1;
1697 ptr2 = strchrW(ptr,',');
1699 else
1700 break;
1704 msi_free(override);
1705 return TRUE;
1708 static BOOL process_overrides( MSIPACKAGE *package, int level )
1710 static const WCHAR szAddLocal[] =
1711 {'A','D','D','L','O','C','A','L',0};
1712 static const WCHAR szAddSource[] =
1713 {'A','D','D','S','O','U','R','C','E',0};
1714 static const WCHAR szAdvertise[] =
1715 {'A','D','V','E','R','T','I','S','E',0};
1716 BOOL ret = FALSE;
1718 /* all these activation/deactivation things happen in order and things
1719 * later on the list override things earlier on the list.
1721 * 0 INSTALLLEVEL processing
1722 * 1 ADDLOCAL
1723 * 2 REMOVE
1724 * 3 ADDSOURCE
1725 * 4 ADDDEFAULT
1726 * 5 REINSTALL
1727 * 6 ADVERTISE
1728 * 7 COMPADDLOCAL
1729 * 8 COMPADDSOURCE
1730 * 9 FILEADDLOCAL
1731 * 10 FILEADDSOURCE
1732 * 11 FILEADDDEFAULT
1734 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1735 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1736 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1737 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1738 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1740 if (ret)
1741 msi_set_property( package->db, szPreselected, szOne );
1743 return ret;
1746 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1748 int level;
1749 MSICOMPONENT* component;
1750 MSIFEATURE *feature;
1752 TRACE("Checking Install Level\n");
1754 level = msi_get_property_int(package->db, szInstallLevel, 1);
1756 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1758 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1760 if (!is_feature_selected( feature, level )) continue;
1762 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1764 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1766 feature->Action = INSTALLSTATE_SOURCE;
1767 feature->ActionRequest = INSTALLSTATE_SOURCE;
1769 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1771 feature->Action = INSTALLSTATE_ADVERTISED;
1772 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1774 else
1776 feature->Action = INSTALLSTATE_LOCAL;
1777 feature->ActionRequest = INSTALLSTATE_LOCAL;
1781 /* disable child features of unselected parent or follow parent */
1782 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1784 FeatureList *fl;
1786 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1788 if (!is_feature_selected( feature, level ))
1790 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1791 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1793 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1795 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1796 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1797 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1798 fl->feature->Action = feature->Action;
1799 fl->feature->ActionRequest = feature->ActionRequest;
1804 else /* preselected */
1806 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1808 if (!is_feature_selected( feature, level )) continue;
1810 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1812 if (feature->Installed == INSTALLSTATE_ABSENT)
1814 feature->Action = INSTALLSTATE_UNKNOWN;
1815 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1817 else
1819 feature->Action = feature->Installed;
1820 feature->ActionRequest = feature->Installed;
1824 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1826 FeatureList *fl;
1828 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1830 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1831 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1833 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1834 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1835 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1836 fl->feature->Action = feature->Action;
1837 fl->feature->ActionRequest = feature->ActionRequest;
1843 /* now we want to set component state based based on feature state */
1844 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1846 ComponentList *cl;
1848 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1849 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1850 feature->ActionRequest, feature->Action);
1852 if (!is_feature_selected( feature, level )) continue;
1854 /* features with components that have compressed files are made local */
1855 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1857 if (cl->component->ForceLocalState &&
1858 feature->ActionRequest == INSTALLSTATE_SOURCE)
1860 feature->Action = INSTALLSTATE_LOCAL;
1861 feature->ActionRequest = INSTALLSTATE_LOCAL;
1862 break;
1866 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1868 component = cl->component;
1870 switch (feature->ActionRequest)
1872 case INSTALLSTATE_ABSENT:
1873 component->anyAbsent = 1;
1874 break;
1875 case INSTALLSTATE_ADVERTISED:
1876 component->hasAdvertiseFeature = 1;
1877 break;
1878 case INSTALLSTATE_SOURCE:
1879 component->hasSourceFeature = 1;
1880 break;
1881 case INSTALLSTATE_LOCAL:
1882 component->hasLocalFeature = 1;
1883 break;
1884 case INSTALLSTATE_DEFAULT:
1885 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1886 component->hasAdvertiseFeature = 1;
1887 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1888 component->hasSourceFeature = 1;
1889 else
1890 component->hasLocalFeature = 1;
1891 break;
1892 default:
1893 break;
1898 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1900 /* check if it's local or source */
1901 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1902 (component->hasLocalFeature || component->hasSourceFeature))
1904 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1905 !component->ForceLocalState)
1907 component->Action = INSTALLSTATE_SOURCE;
1908 component->ActionRequest = INSTALLSTATE_SOURCE;
1910 else
1912 component->Action = INSTALLSTATE_LOCAL;
1913 component->ActionRequest = INSTALLSTATE_LOCAL;
1915 continue;
1918 /* if any feature is local, the component must be local too */
1919 if (component->hasLocalFeature)
1921 component->Action = INSTALLSTATE_LOCAL;
1922 component->ActionRequest = INSTALLSTATE_LOCAL;
1923 continue;
1925 if (component->hasSourceFeature)
1927 component->Action = INSTALLSTATE_SOURCE;
1928 component->ActionRequest = INSTALLSTATE_SOURCE;
1929 continue;
1931 if (component->hasAdvertiseFeature)
1933 component->Action = INSTALLSTATE_ADVERTISED;
1934 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1935 continue;
1937 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1938 if (component->anyAbsent &&
1939 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1941 component->Action = INSTALLSTATE_ABSENT;
1942 component->ActionRequest = INSTALLSTATE_ABSENT;
1946 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1948 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1950 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1951 component->Action = INSTALLSTATE_LOCAL;
1952 component->ActionRequest = INSTALLSTATE_LOCAL;
1955 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1956 component->Installed == INSTALLSTATE_SOURCE &&
1957 component->hasSourceFeature)
1959 component->Action = INSTALLSTATE_UNKNOWN;
1960 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1963 TRACE("component %s (installed %d request %d action %d)\n",
1964 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1967 return ERROR_SUCCESS;
1970 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1972 MSIPACKAGE *package = param;
1973 LPCWSTR name;
1974 MSIFEATURE *feature;
1976 name = MSI_RecordGetString( row, 1 );
1978 feature = msi_get_loaded_feature( package, name );
1979 if (!feature)
1980 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1981 else
1983 LPCWSTR Condition;
1984 Condition = MSI_RecordGetString(row,3);
1986 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1988 int level = MSI_RecordGetInteger(row,2);
1989 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1990 feature->Level = level;
1993 return ERROR_SUCCESS;
1996 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
1998 static const WCHAR name[] = {'\\',0};
1999 VS_FIXEDFILEINFO *ptr, *ret;
2000 LPVOID version;
2001 DWORD versize, handle;
2002 UINT sz;
2004 versize = GetFileVersionInfoSizeW( filename, &handle );
2005 if (!versize)
2006 return NULL;
2008 version = msi_alloc( versize );
2009 if (!version)
2010 return NULL;
2012 GetFileVersionInfoW( filename, 0, versize, version );
2014 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2016 msi_free( version );
2017 return NULL;
2020 ret = msi_alloc( sz );
2021 memcpy( ret, ptr, sz );
2023 msi_free( version );
2024 return ret;
2027 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2029 DWORD ms, ls;
2031 msi_parse_version_string( version, &ms, &ls );
2033 if (fi->dwFileVersionMS > ms) return 1;
2034 else if (fi->dwFileVersionMS < ms) return -1;
2035 else if (fi->dwFileVersionLS > ls) return 1;
2036 else if (fi->dwFileVersionLS < ls) return -1;
2037 return 0;
2040 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2042 DWORD ms1, ms2;
2044 msi_parse_version_string( ver1, &ms1, NULL );
2045 msi_parse_version_string( ver2, &ms2, NULL );
2047 if (ms1 > ms2) return 1;
2048 else if (ms1 < ms2) return -1;
2049 return 0;
2052 DWORD msi_get_disk_file_size( LPCWSTR filename )
2054 HANDLE file;
2055 DWORD size;
2057 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2058 if (file == INVALID_HANDLE_VALUE)
2059 return INVALID_FILE_SIZE;
2061 size = GetFileSize( file, NULL );
2062 TRACE("size is %u\n", size);
2063 CloseHandle( file );
2064 return size;
2067 BOOL msi_file_hash_matches( MSIFILE *file )
2069 UINT r;
2070 MSIFILEHASHINFO hash;
2072 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2073 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2074 if (r != ERROR_SUCCESS)
2075 return FALSE;
2077 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2080 static WCHAR *get_temp_dir( void )
2082 static UINT id;
2083 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2085 GetTempPathW( MAX_PATH, tmp );
2086 for (;;)
2088 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2089 if (CreateDirectoryW( dir, NULL )) break;
2091 return strdupW( dir );
2095 * msi_build_directory_name()
2097 * This function is to save messing round with directory names
2098 * It handles adding backslashes between path segments,
2099 * and can add \ at the end of the directory name if told to.
2101 * It takes a variable number of arguments.
2102 * It always allocates a new string for the result, so make sure
2103 * to free the return value when finished with it.
2105 * The first arg is the number of path segments that follow.
2106 * The arguments following count are a list of path segments.
2107 * A path segment may be NULL.
2109 * Path segments will be added with a \ separating them.
2110 * A \ will not be added after the last segment, however if the
2111 * last segment is NULL, then the last character will be a \
2113 WCHAR *msi_build_directory_name( DWORD count, ... )
2115 DWORD sz = 1, i;
2116 WCHAR *dir;
2117 va_list va;
2119 va_start( va, count );
2120 for (i = 0; i < count; i++)
2122 const WCHAR *str = va_arg( va, const WCHAR * );
2123 if (str) sz += strlenW( str ) + 1;
2125 va_end( va );
2127 dir = msi_alloc( sz * sizeof(WCHAR) );
2128 dir[0] = 0;
2130 va_start( va, count );
2131 for (i = 0; i < count; i++)
2133 const WCHAR *str = va_arg( va, const WCHAR * );
2134 if (!str) continue;
2135 strcatW( dir, str );
2136 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2138 va_end( va );
2139 return dir;
2142 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2144 MSIASSEMBLY *assembly = file->Component->assembly;
2146 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2148 msi_free( file->TargetPath );
2149 if (assembly && !assembly->application)
2151 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2152 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2153 msi_track_tempfile( package, file->TargetPath );
2155 else
2157 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2158 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2161 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2164 static UINT calculate_file_cost( MSIPACKAGE *package )
2166 VS_FIXEDFILEINFO *file_version;
2167 WCHAR *font_version;
2168 MSIFILE *file;
2170 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2172 MSICOMPONENT *comp = file->Component;
2173 DWORD file_size;
2175 if (!comp->Enabled) continue;
2177 if (file->IsCompressed)
2178 comp->ForceLocalState = TRUE;
2180 set_target_path( package, file );
2182 if ((comp->assembly && !comp->assembly->installed) ||
2183 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2185 comp->Cost += file->FileSize;
2186 continue;
2188 file_size = msi_get_disk_file_size( file->TargetPath );
2190 if (file->Version)
2192 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2194 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2196 comp->Cost += file->FileSize - file_size;
2198 msi_free( file_version );
2199 continue;
2201 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2203 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2205 comp->Cost += file->FileSize - file_size;
2207 msi_free( font_version );
2208 continue;
2211 if (file_size != file->FileSize)
2213 comp->Cost += file->FileSize - file_size;
2216 return ERROR_SUCCESS;
2219 WCHAR *msi_normalize_path( const WCHAR *in )
2221 const WCHAR *p = in;
2222 WCHAR *q, *ret;
2223 int n, len = strlenW( in ) + 2;
2225 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2227 len = 0;
2228 while (1)
2230 /* copy until the end of the string or a space */
2231 while (*p != ' ' && (*q = *p))
2233 p++, len++;
2234 /* reduce many backslashes to one */
2235 if (*p != '\\' || *q != '\\')
2236 q++;
2239 /* quit at the end of the string */
2240 if (!*p)
2241 break;
2243 /* count the number of spaces */
2244 n = 0;
2245 while (p[n] == ' ')
2246 n++;
2248 /* if it's leading or trailing space, skip it */
2249 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2250 p += n;
2251 else /* copy n spaces */
2252 while (n && (*q++ = *p++)) n--;
2254 while (q - ret > 0 && q[-1] == ' ') q--;
2255 if (q - ret > 0 && q[-1] != '\\')
2257 q[0] = '\\';
2258 q[1] = 0;
2260 return ret;
2263 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2265 FolderList *fl;
2266 MSIFOLDER *folder, *parent, *child;
2267 WCHAR *path, *normalized_path;
2269 TRACE("resolving %s\n", debugstr_w(name));
2271 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2273 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2275 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
2277 path = msi_dup_property( package->db, szRootDrive );
2280 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2282 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2284 parent = msi_get_loaded_folder( package, folder->Parent );
2285 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2287 else
2288 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2290 normalized_path = msi_normalize_path( path );
2291 msi_free( path );
2292 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2294 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2295 msi_free( normalized_path );
2296 return;
2298 msi_set_property( package->db, folder->Directory, normalized_path );
2299 msi_free( folder->ResolvedTarget );
2300 folder->ResolvedTarget = normalized_path;
2302 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2304 child = fl->folder;
2305 msi_resolve_target_folder( package, child->Directory, load_prop );
2307 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2310 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2312 static const WCHAR query[] = {
2313 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2314 '`','C','o','n','d','i','t','i','o','n','`',0};
2315 static const WCHAR szOutOfDiskSpace[] = {
2316 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2317 MSICOMPONENT *comp;
2318 MSIQUERY *view;
2319 LPWSTR level;
2320 UINT rc;
2322 TRACE("Building directory properties\n");
2323 msi_resolve_target_folder( package, szTargetDir, TRUE );
2325 TRACE("Evaluating component conditions\n");
2326 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2328 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2330 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2331 comp->Enabled = FALSE;
2333 else
2334 comp->Enabled = TRUE;
2337 /* read components states from the registry */
2338 ACTION_GetComponentInstallStates(package);
2339 ACTION_GetFeatureInstallStates(package);
2341 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2343 TRACE("Evaluating feature conditions\n");
2345 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2346 if (rc == ERROR_SUCCESS)
2348 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2349 msiobj_release( &view->hdr );
2350 if (rc != ERROR_SUCCESS)
2351 return rc;
2355 TRACE("Calculating file cost\n");
2356 calculate_file_cost( package );
2358 msi_set_property( package->db, szCostingComplete, szOne );
2359 /* set default run level if not set */
2360 level = msi_dup_property( package->db, szInstallLevel );
2361 if (!level)
2362 msi_set_property( package->db, szInstallLevel, szOne );
2363 msi_free(level);
2365 /* FIXME: check volume disk space */
2366 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2368 return MSI_SetFeatureStates(package);
2371 /* OK this value is "interpreted" and then formatted based on the
2372 first few characters */
2373 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2374 DWORD *size)
2376 LPSTR data = NULL;
2378 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2380 if (value[1]=='x')
2382 LPWSTR ptr;
2383 CHAR byte[5];
2384 LPWSTR deformated = NULL;
2385 int count;
2387 deformat_string(package, &value[2], &deformated);
2389 /* binary value type */
2390 ptr = deformated;
2391 *type = REG_BINARY;
2392 if (strlenW(ptr)%2)
2393 *size = (strlenW(ptr)/2)+1;
2394 else
2395 *size = strlenW(ptr)/2;
2397 data = msi_alloc(*size);
2399 byte[0] = '0';
2400 byte[1] = 'x';
2401 byte[4] = 0;
2402 count = 0;
2403 /* if uneven pad with a zero in front */
2404 if (strlenW(ptr)%2)
2406 byte[2]= '0';
2407 byte[3]= *ptr;
2408 ptr++;
2409 data[count] = (BYTE)strtol(byte,NULL,0);
2410 count ++;
2411 TRACE("Uneven byte count\n");
2413 while (*ptr)
2415 byte[2]= *ptr;
2416 ptr++;
2417 byte[3]= *ptr;
2418 ptr++;
2419 data[count] = (BYTE)strtol(byte,NULL,0);
2420 count ++;
2422 msi_free(deformated);
2424 TRACE("Data %i bytes(%i)\n",*size,count);
2426 else
2428 LPWSTR deformated;
2429 LPWSTR p;
2430 DWORD d = 0;
2431 deformat_string(package, &value[1], &deformated);
2433 *type=REG_DWORD;
2434 *size = sizeof(DWORD);
2435 data = msi_alloc(*size);
2436 p = deformated;
2437 if (*p == '-')
2438 p++;
2439 while (*p)
2441 if ( (*p < '0') || (*p > '9') )
2442 break;
2443 d *= 10;
2444 d += (*p - '0');
2445 p++;
2447 if (deformated[0] == '-')
2448 d = -d;
2449 *(LPDWORD)data = d;
2450 TRACE("DWORD %i\n",*(LPDWORD)data);
2452 msi_free(deformated);
2455 else
2457 static const WCHAR szMulti[] = {'[','~',']',0};
2458 LPCWSTR ptr;
2459 *type=REG_SZ;
2461 if (value[0]=='#')
2463 if (value[1]=='%')
2465 ptr = &value[2];
2466 *type=REG_EXPAND_SZ;
2468 else
2469 ptr = &value[1];
2471 else
2472 ptr=value;
2474 if (strstrW(value, szMulti))
2475 *type = REG_MULTI_SZ;
2477 /* remove initial delimiter */
2478 if (!strncmpW(value, szMulti, 3))
2479 ptr = value + 3;
2481 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2483 /* add double NULL terminator */
2484 if (*type == REG_MULTI_SZ)
2486 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2487 data = msi_realloc_zero(data, *size);
2490 return data;
2493 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2495 const WCHAR *ret;
2497 switch (root)
2499 case -1:
2500 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2502 *root_key = HKEY_LOCAL_MACHINE;
2503 ret = szHLM;
2505 else
2507 *root_key = HKEY_CURRENT_USER;
2508 ret = szHCU;
2510 break;
2511 case 0:
2512 *root_key = HKEY_CLASSES_ROOT;
2513 ret = szHCR;
2514 break;
2515 case 1:
2516 *root_key = HKEY_CURRENT_USER;
2517 ret = szHCU;
2518 break;
2519 case 2:
2520 *root_key = HKEY_LOCAL_MACHINE;
2521 ret = szHLM;
2522 break;
2523 case 3:
2524 *root_key = HKEY_USERS;
2525 ret = szHU;
2526 break;
2527 default:
2528 ERR("Unknown root %i\n", root);
2529 return NULL;
2532 return ret;
2535 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2537 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2538 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2540 if (is_64bit && package->platform == PLATFORM_INTEL &&
2541 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2543 UINT size;
2544 WCHAR *path_32node;
2546 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2547 if (!(path_32node = msi_alloc( size ))) return NULL;
2549 memcpy( path_32node, path, len * sizeof(WCHAR) );
2550 strcpyW( path_32node + len, szWow6432Node );
2551 strcatW( path_32node, szBackSlash );
2552 strcatW( path_32node, path + len );
2553 return path_32node;
2556 return strdupW( path );
2559 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2561 MSIPACKAGE *package = param;
2562 LPSTR value_data = NULL;
2563 HKEY root_key, hkey;
2564 DWORD type,size;
2565 LPWSTR deformated, uikey, keypath;
2566 LPCWSTR szRoot, component, name, key, value;
2567 MSICOMPONENT *comp;
2568 MSIRECORD * uirow;
2569 INT root;
2570 BOOL check_first = FALSE;
2571 UINT rc;
2573 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2575 component = MSI_RecordGetString(row, 6);
2576 comp = msi_get_loaded_component(package,component);
2577 if (!comp)
2578 return ERROR_SUCCESS;
2580 comp->Action = msi_get_component_action( package, comp );
2581 if (comp->Action != INSTALLSTATE_LOCAL)
2583 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2584 return ERROR_SUCCESS;
2587 name = MSI_RecordGetString(row, 4);
2588 if( MSI_RecordIsNull(row,5) && name )
2590 /* null values can have special meanings */
2591 if (name[0]=='-' && name[1] == 0)
2592 return ERROR_SUCCESS;
2593 else if ((name[0]=='+' && name[1] == 0) ||
2594 (name[0] == '*' && name[1] == 0))
2595 name = NULL;
2596 check_first = TRUE;
2599 root = MSI_RecordGetInteger(row,2);
2600 key = MSI_RecordGetString(row, 3);
2602 szRoot = get_root_key( package, root, &root_key );
2603 if (!szRoot)
2604 return ERROR_SUCCESS;
2606 deformat_string(package, key , &deformated);
2607 size = strlenW(deformated) + strlenW(szRoot) + 1;
2608 uikey = msi_alloc(size*sizeof(WCHAR));
2609 strcpyW(uikey,szRoot);
2610 strcatW(uikey,deformated);
2612 keypath = get_keypath( package, root_key, deformated );
2613 msi_free( deformated );
2614 if (RegCreateKeyW( root_key, keypath, &hkey ))
2616 ERR("Could not create key %s\n", debugstr_w(keypath));
2617 msi_free(uikey);
2618 msi_free(keypath);
2619 return ERROR_SUCCESS;
2622 value = MSI_RecordGetString(row,5);
2623 if (value)
2624 value_data = parse_value(package, value, &type, &size);
2625 else
2627 value_data = (LPSTR)strdupW(szEmpty);
2628 size = sizeof(szEmpty);
2629 type = REG_SZ;
2632 deformat_string(package, name, &deformated);
2634 if (!check_first)
2636 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2637 debugstr_w(uikey));
2638 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2640 else
2642 DWORD sz = 0;
2643 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2644 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2646 TRACE("value %s of %s checked already exists\n",
2647 debugstr_w(deformated), debugstr_w(uikey));
2649 else
2651 TRACE("Checked and setting value %s of %s\n",
2652 debugstr_w(deformated), debugstr_w(uikey));
2653 if (deformated || size)
2654 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2657 RegCloseKey(hkey);
2659 uirow = MSI_CreateRecord(3);
2660 MSI_RecordSetStringW(uirow,2,deformated);
2661 MSI_RecordSetStringW(uirow,1,uikey);
2662 if (type == REG_SZ || type == REG_EXPAND_SZ)
2663 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2664 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2665 msiobj_release( &uirow->hdr );
2667 msi_free(value_data);
2668 msi_free(deformated);
2669 msi_free(uikey);
2670 msi_free(keypath);
2672 return ERROR_SUCCESS;
2675 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2677 static const WCHAR query[] = {
2678 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2679 '`','R','e','g','i','s','t','r','y','`',0};
2680 MSIQUERY *view;
2681 UINT rc;
2683 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2684 if (rc != ERROR_SUCCESS)
2685 return ERROR_SUCCESS;
2687 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2688 msiobj_release(&view->hdr);
2689 return rc;
2692 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2694 LONG res;
2695 HKEY hkey;
2696 DWORD num_subkeys, num_values;
2698 if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2700 if ((res = RegDeleteValueW( hkey, value )))
2702 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2704 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2705 NULL, NULL, NULL, NULL );
2706 RegCloseKey( hkey );
2707 if (!res && !num_subkeys && !num_values)
2709 TRACE("removing empty key %s\n", debugstr_w(keypath));
2710 RegDeleteKeyW( root, keypath );
2712 return;
2714 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2717 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2719 LONG res = RegDeleteTreeW( root, keypath );
2720 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2723 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2725 MSIPACKAGE *package = param;
2726 LPCWSTR component, name, key_str, root_key_str;
2727 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2728 MSICOMPONENT *comp;
2729 MSIRECORD *uirow;
2730 BOOL delete_key = FALSE;
2731 HKEY hkey_root;
2732 UINT size;
2733 INT root;
2735 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2737 component = MSI_RecordGetString( row, 6 );
2738 comp = msi_get_loaded_component( package, component );
2739 if (!comp)
2740 return ERROR_SUCCESS;
2742 comp->Action = msi_get_component_action( package, comp );
2743 if (comp->Action != INSTALLSTATE_ABSENT)
2745 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2746 return ERROR_SUCCESS;
2749 name = MSI_RecordGetString( row, 4 );
2750 if (MSI_RecordIsNull( row, 5 ) && name )
2752 if (name[0] == '+' && !name[1])
2753 return ERROR_SUCCESS;
2754 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2756 delete_key = TRUE;
2757 name = NULL;
2761 root = MSI_RecordGetInteger( row, 2 );
2762 key_str = MSI_RecordGetString( row, 3 );
2764 root_key_str = get_root_key( package, root, &hkey_root );
2765 if (!root_key_str)
2766 return ERROR_SUCCESS;
2768 deformat_string( package, key_str, &deformated_key );
2769 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2770 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2771 strcpyW( ui_key_str, root_key_str );
2772 strcatW( ui_key_str, deformated_key );
2774 deformat_string( package, name, &deformated_name );
2776 keypath = get_keypath( package, hkey_root, deformated_key );
2777 msi_free( deformated_key );
2778 if (delete_key) delete_reg_key( hkey_root, keypath );
2779 else delete_reg_value( hkey_root, keypath, deformated_name );
2780 msi_free( keypath );
2782 uirow = MSI_CreateRecord( 2 );
2783 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2784 MSI_RecordSetStringW( uirow, 2, deformated_name );
2785 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2786 msiobj_release( &uirow->hdr );
2788 msi_free( ui_key_str );
2789 msi_free( deformated_name );
2790 return ERROR_SUCCESS;
2793 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2795 MSIPACKAGE *package = param;
2796 LPCWSTR component, name, key_str, root_key_str;
2797 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2798 MSICOMPONENT *comp;
2799 MSIRECORD *uirow;
2800 BOOL delete_key = FALSE;
2801 HKEY hkey_root;
2802 UINT size;
2803 INT root;
2805 component = MSI_RecordGetString( row, 5 );
2806 comp = msi_get_loaded_component( package, component );
2807 if (!comp)
2808 return ERROR_SUCCESS;
2810 comp->Action = msi_get_component_action( package, comp );
2811 if (comp->Action != INSTALLSTATE_LOCAL)
2813 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2814 return ERROR_SUCCESS;
2817 if ((name = MSI_RecordGetString( row, 4 )))
2819 if (name[0] == '-' && !name[1])
2821 delete_key = TRUE;
2822 name = NULL;
2826 root = MSI_RecordGetInteger( row, 2 );
2827 key_str = MSI_RecordGetString( row, 3 );
2829 root_key_str = get_root_key( package, root, &hkey_root );
2830 if (!root_key_str)
2831 return ERROR_SUCCESS;
2833 deformat_string( package, key_str, &deformated_key );
2834 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2835 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2836 strcpyW( ui_key_str, root_key_str );
2837 strcatW( ui_key_str, deformated_key );
2839 deformat_string( package, name, &deformated_name );
2841 keypath = get_keypath( package, hkey_root, deformated_key );
2842 msi_free( deformated_key );
2843 if (delete_key) delete_reg_key( hkey_root, keypath );
2844 else delete_reg_value( hkey_root, keypath, deformated_name );
2845 msi_free( keypath );
2847 uirow = MSI_CreateRecord( 2 );
2848 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2849 MSI_RecordSetStringW( uirow, 2, deformated_name );
2850 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2851 msiobj_release( &uirow->hdr );
2853 msi_free( ui_key_str );
2854 msi_free( deformated_name );
2855 return ERROR_SUCCESS;
2858 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2860 static const WCHAR registry_query[] = {
2861 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2862 '`','R','e','g','i','s','t','r','y','`',0};
2863 static const WCHAR remove_registry_query[] = {
2864 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2865 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2866 MSIQUERY *view;
2867 UINT rc;
2869 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2870 if (rc == ERROR_SUCCESS)
2872 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2873 msiobj_release( &view->hdr );
2874 if (rc != ERROR_SUCCESS)
2875 return rc;
2877 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2878 if (rc == ERROR_SUCCESS)
2880 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2881 msiobj_release( &view->hdr );
2882 if (rc != ERROR_SUCCESS)
2883 return rc;
2885 return ERROR_SUCCESS;
2888 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2890 package->script->CurrentlyScripting = TRUE;
2892 return ERROR_SUCCESS;
2896 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2898 static const WCHAR query[]= {
2899 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2900 '`','R','e','g','i','s','t','r','y','`',0};
2901 MSICOMPONENT *comp;
2902 DWORD total = 0, count = 0;
2903 MSIQUERY *view;
2904 MSIFEATURE *feature;
2905 MSIFILE *file;
2906 UINT rc;
2908 TRACE("InstallValidate\n");
2910 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2911 if (rc == ERROR_SUCCESS)
2913 rc = MSI_IterateRecords( view, &count, NULL, package );
2914 msiobj_release( &view->hdr );
2915 if (rc != ERROR_SUCCESS)
2916 return rc;
2917 total += count * REG_PROGRESS_VALUE;
2919 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2920 total += COMPONENT_PROGRESS_VALUE;
2922 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2923 total += file->FileSize;
2925 msi_ui_progress( package, 0, total, 0, 0 );
2927 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2929 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2930 debugstr_w(feature->Feature), feature->Installed,
2931 feature->ActionRequest, feature->Action);
2933 return ERROR_SUCCESS;
2936 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2938 MSIPACKAGE* package = param;
2939 LPCWSTR cond = NULL;
2940 LPCWSTR message = NULL;
2941 UINT r;
2943 static const WCHAR title[]=
2944 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2946 cond = MSI_RecordGetString(row,1);
2948 r = MSI_EvaluateConditionW(package,cond);
2949 if (r == MSICONDITION_FALSE)
2951 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2953 LPWSTR deformated;
2954 message = MSI_RecordGetString(row,2);
2955 deformat_string(package,message,&deformated);
2956 MessageBoxW(NULL,deformated,title,MB_OK);
2957 msi_free(deformated);
2960 return ERROR_INSTALL_FAILURE;
2963 return ERROR_SUCCESS;
2966 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2968 static const WCHAR query[] = {
2969 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2970 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2971 MSIQUERY *view;
2972 UINT rc;
2974 TRACE("Checking launch conditions\n");
2976 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2977 if (rc != ERROR_SUCCESS)
2978 return ERROR_SUCCESS;
2980 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2981 msiobj_release(&view->hdr);
2982 return rc;
2985 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2988 if (!cmp->KeyPath)
2989 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2991 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2993 static const WCHAR query[] = {
2994 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2995 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
2996 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
2997 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
2998 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2999 MSIRECORD *row;
3000 UINT root, len;
3001 LPWSTR deformated, buffer, deformated_name;
3002 LPCWSTR key, name;
3004 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3005 if (!row)
3006 return NULL;
3008 root = MSI_RecordGetInteger(row,2);
3009 key = MSI_RecordGetString(row, 3);
3010 name = MSI_RecordGetString(row, 4);
3011 deformat_string(package, key , &deformated);
3012 deformat_string(package, name, &deformated_name);
3014 len = strlenW(deformated) + 6;
3015 if (deformated_name)
3016 len+=strlenW(deformated_name);
3018 buffer = msi_alloc( len *sizeof(WCHAR));
3020 if (deformated_name)
3021 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3022 else
3023 sprintfW(buffer,fmt,root,deformated);
3025 msi_free(deformated);
3026 msi_free(deformated_name);
3027 msiobj_release(&row->hdr);
3029 return buffer;
3031 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3033 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3034 return NULL;
3036 else
3038 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3040 if (file)
3041 return strdupW( file->TargetPath );
3043 return NULL;
3046 static HKEY openSharedDLLsKey(void)
3048 HKEY hkey=0;
3049 static const WCHAR path[] =
3050 {'S','o','f','t','w','a','r','e','\\',
3051 'M','i','c','r','o','s','o','f','t','\\',
3052 'W','i','n','d','o','w','s','\\',
3053 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3054 'S','h','a','r','e','d','D','L','L','s',0};
3056 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3057 return hkey;
3060 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3062 HKEY hkey;
3063 DWORD count=0;
3064 DWORD type;
3065 DWORD sz = sizeof(count);
3066 DWORD rc;
3068 hkey = openSharedDLLsKey();
3069 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3070 if (rc != ERROR_SUCCESS)
3071 count = 0;
3072 RegCloseKey(hkey);
3073 return count;
3076 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3078 HKEY hkey;
3080 hkey = openSharedDLLsKey();
3081 if (count > 0)
3082 msi_reg_set_val_dword( hkey, path, count );
3083 else
3084 RegDeleteValueW(hkey,path);
3085 RegCloseKey(hkey);
3086 return count;
3089 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3091 MSIFEATURE *feature;
3092 INT count = 0;
3093 BOOL write = FALSE;
3095 /* only refcount DLLs */
3096 if (comp->KeyPath == NULL ||
3097 comp->assembly ||
3098 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3099 comp->Attributes & msidbComponentAttributesODBCDataSource)
3100 write = FALSE;
3101 else
3103 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3104 write = (count > 0);
3106 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3107 write = TRUE;
3110 /* increment counts */
3111 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3113 ComponentList *cl;
3115 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3116 continue;
3118 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3120 if ( cl->component == comp )
3121 count++;
3125 /* decrement counts */
3126 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3128 ComponentList *cl;
3130 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3131 continue;
3133 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3135 if ( cl->component == comp )
3136 count--;
3140 /* ref count all the files in the component */
3141 if (write)
3143 MSIFILE *file;
3145 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3147 if (file->Component == comp)
3148 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3152 /* add a count for permanent */
3153 if (comp->Attributes & msidbComponentAttributesPermanent)
3154 count ++;
3156 comp->RefCount = count;
3158 if (write)
3159 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3162 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3164 if (comp->assembly)
3166 const WCHAR prefixW[] = {'<','\\',0};
3167 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3168 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3170 if (keypath)
3172 strcpyW( keypath, prefixW );
3173 strcatW( keypath, comp->assembly->display_name );
3175 return keypath;
3177 return resolve_keypath( package, comp );
3180 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3182 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3183 UINT rc;
3184 MSICOMPONENT *comp;
3185 HKEY hkey;
3187 TRACE("\n");
3189 squash_guid(package->ProductCode,squished_pc);
3190 msi_set_sourcedir_props(package, FALSE);
3192 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3194 MSIRECORD *uirow;
3195 INSTALLSTATE action;
3197 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3198 if (!comp->ComponentId)
3199 continue;
3201 squash_guid( comp->ComponentId, squished_cc );
3202 msi_free( comp->FullKeypath );
3203 comp->FullKeypath = build_full_keypath( package, comp );
3205 ACTION_RefCountComponent( package, comp );
3207 if (package->need_rollback) action = comp->Installed;
3208 else action = comp->ActionRequest;
3210 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3211 debugstr_w(comp->Component), debugstr_w(squished_cc),
3212 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3214 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3216 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3217 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3218 else
3219 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3221 if (rc != ERROR_SUCCESS)
3222 continue;
3224 if (comp->Attributes & msidbComponentAttributesPermanent)
3226 static const WCHAR szPermKey[] =
3227 { '0','0','0','0','0','0','0','0','0','0','0','0',
3228 '0','0','0','0','0','0','0','0','0','0','0','0',
3229 '0','0','0','0','0','0','0','0',0 };
3231 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3233 if (action == INSTALLSTATE_LOCAL)
3234 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3235 else
3237 MSIFILE *file;
3238 MSIRECORD *row;
3239 LPWSTR ptr, ptr2;
3240 WCHAR source[MAX_PATH];
3241 WCHAR base[MAX_PATH];
3242 LPWSTR sourcepath;
3244 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3245 static const WCHAR query[] = {
3246 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3247 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3248 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3249 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3250 '`','D','i','s','k','I','d','`',0};
3252 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3253 continue;
3255 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3256 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3257 ptr2 = strrchrW(source, '\\') + 1;
3258 msiobj_release(&row->hdr);
3260 lstrcpyW(base, package->PackagePath);
3261 ptr = strrchrW(base, '\\');
3262 *(ptr + 1) = '\0';
3264 sourcepath = msi_resolve_file_source(package, file);
3265 ptr = sourcepath + lstrlenW(base);
3266 lstrcpyW(ptr2, ptr);
3267 msi_free(sourcepath);
3269 msi_reg_set_val_str(hkey, squished_pc, source);
3271 RegCloseKey(hkey);
3273 else if (action == INSTALLSTATE_ABSENT)
3275 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3276 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3277 else
3278 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3281 /* UI stuff */
3282 uirow = MSI_CreateRecord(3);
3283 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3284 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3285 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3286 msi_ui_actiondata( package, szProcessComponents, uirow );
3287 msiobj_release( &uirow->hdr );
3289 return ERROR_SUCCESS;
3292 typedef struct {
3293 CLSID clsid;
3294 LPWSTR source;
3296 LPWSTR path;
3297 ITypeLib *ptLib;
3298 } typelib_struct;
3300 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3301 LPWSTR lpszName, LONG_PTR lParam)
3303 TLIBATTR *attr;
3304 typelib_struct *tl_struct = (typelib_struct*) lParam;
3305 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3306 int sz;
3307 HRESULT res;
3309 if (!IS_INTRESOURCE(lpszName))
3311 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3312 return TRUE;
3315 sz = strlenW(tl_struct->source)+4;
3316 sz *= sizeof(WCHAR);
3318 if ((INT_PTR)lpszName == 1)
3319 tl_struct->path = strdupW(tl_struct->source);
3320 else
3322 tl_struct->path = msi_alloc(sz);
3323 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3326 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3327 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3328 if (FAILED(res))
3330 msi_free(tl_struct->path);
3331 tl_struct->path = NULL;
3333 return TRUE;
3336 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3337 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3339 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3340 return FALSE;
3343 msi_free(tl_struct->path);
3344 tl_struct->path = NULL;
3346 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3347 ITypeLib_Release(tl_struct->ptLib);
3349 return TRUE;
3352 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3354 MSIPACKAGE* package = param;
3355 LPCWSTR component;
3356 MSICOMPONENT *comp;
3357 MSIFILE *file;
3358 typelib_struct tl_struct;
3359 ITypeLib *tlib;
3360 HMODULE module;
3361 HRESULT hr;
3363 component = MSI_RecordGetString(row,3);
3364 comp = msi_get_loaded_component(package,component);
3365 if (!comp)
3366 return ERROR_SUCCESS;
3368 comp->Action = msi_get_component_action( package, comp );
3369 if (comp->Action != INSTALLSTATE_LOCAL)
3371 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3372 return ERROR_SUCCESS;
3375 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3377 TRACE("component has no key path\n");
3378 return ERROR_SUCCESS;
3380 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3382 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3383 if (module)
3385 LPCWSTR guid;
3386 guid = MSI_RecordGetString(row,1);
3387 CLSIDFromString( guid, &tl_struct.clsid);
3388 tl_struct.source = strdupW( file->TargetPath );
3389 tl_struct.path = NULL;
3391 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3392 (LONG_PTR)&tl_struct);
3394 if (tl_struct.path)
3396 LPCWSTR helpid, help_path = NULL;
3397 HRESULT res;
3399 helpid = MSI_RecordGetString(row,6);
3401 if (helpid) help_path = msi_get_target_folder( package, helpid );
3402 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3404 if (FAILED(res))
3405 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3406 else
3407 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3409 ITypeLib_Release(tl_struct.ptLib);
3410 msi_free(tl_struct.path);
3412 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3414 FreeLibrary(module);
3415 msi_free(tl_struct.source);
3417 else
3419 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3420 if (FAILED(hr))
3422 ERR("Failed to load type library: %08x\n", hr);
3423 return ERROR_INSTALL_FAILURE;
3426 ITypeLib_Release(tlib);
3429 return ERROR_SUCCESS;
3432 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3434 static const WCHAR query[] = {
3435 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3436 '`','T','y','p','e','L','i','b','`',0};
3437 MSIQUERY *view;
3438 UINT rc;
3440 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3441 if (rc != ERROR_SUCCESS)
3442 return ERROR_SUCCESS;
3444 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3445 msiobj_release(&view->hdr);
3446 return rc;
3449 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3451 MSIPACKAGE *package = param;
3452 LPCWSTR component, guid;
3453 MSICOMPONENT *comp;
3454 GUID libid;
3455 UINT version;
3456 LCID language;
3457 SYSKIND syskind;
3458 HRESULT hr;
3460 component = MSI_RecordGetString( row, 3 );
3461 comp = msi_get_loaded_component( package, component );
3462 if (!comp)
3463 return ERROR_SUCCESS;
3465 comp->Action = msi_get_component_action( package, comp );
3466 if (comp->Action != INSTALLSTATE_ABSENT)
3468 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3469 return ERROR_SUCCESS;
3471 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3473 guid = MSI_RecordGetString( row, 1 );
3474 CLSIDFromString( guid, &libid );
3475 version = MSI_RecordGetInteger( row, 4 );
3476 language = MSI_RecordGetInteger( row, 2 );
3478 #ifdef _WIN64
3479 syskind = SYS_WIN64;
3480 #else
3481 syskind = SYS_WIN32;
3482 #endif
3484 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3485 if (FAILED(hr))
3487 WARN("Failed to unregister typelib: %08x\n", hr);
3490 return ERROR_SUCCESS;
3493 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3495 static const WCHAR query[] = {
3496 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3497 '`','T','y','p','e','L','i','b','`',0};
3498 MSIQUERY *view;
3499 UINT rc;
3501 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3502 if (rc != ERROR_SUCCESS)
3503 return ERROR_SUCCESS;
3505 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3506 msiobj_release( &view->hdr );
3507 return rc;
3510 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3512 static const WCHAR szlnk[] = {'.','l','n','k',0};
3513 LPCWSTR directory, extension, link_folder;
3514 LPWSTR link_file, filename;
3516 directory = MSI_RecordGetString( row, 2 );
3517 link_folder = msi_get_target_folder( package, directory );
3518 if (!link_folder)
3520 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3521 return NULL;
3523 /* may be needed because of a bug somewhere else */
3524 msi_create_full_path( link_folder );
3526 filename = msi_dup_record_field( row, 3 );
3527 msi_reduce_to_long_filename( filename );
3529 extension = strchrW( filename, '.' );
3530 if (!extension || strcmpiW( extension, szlnk ))
3532 int len = strlenW( filename );
3533 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3534 memcpy( filename + len, szlnk, sizeof(szlnk) );
3536 link_file = msi_build_directory_name( 2, link_folder, filename );
3537 msi_free( filename );
3539 return link_file;
3542 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3544 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3545 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3546 WCHAR *folder, *dest, *path;
3548 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3549 folder = msi_dup_property( package->db, szWindowsFolder );
3550 else
3552 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3553 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3554 msi_free( appdata );
3556 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3557 msi_create_full_path( dest );
3558 path = msi_build_directory_name( 2, dest, icon_name );
3559 msi_free( folder );
3560 msi_free( dest );
3561 return path;
3564 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3566 MSIPACKAGE *package = param;
3567 LPWSTR link_file, deformated, path;
3568 LPCWSTR component, target;
3569 MSICOMPONENT *comp;
3570 IShellLinkW *sl = NULL;
3571 IPersistFile *pf = NULL;
3572 HRESULT res;
3574 component = MSI_RecordGetString(row, 4);
3575 comp = msi_get_loaded_component(package, component);
3576 if (!comp)
3577 return ERROR_SUCCESS;
3579 comp->Action = msi_get_component_action( package, comp );
3580 if (comp->Action != INSTALLSTATE_LOCAL)
3582 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3583 return ERROR_SUCCESS;
3585 msi_ui_actiondata( package, szCreateShortcuts, row );
3587 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3588 &IID_IShellLinkW, (LPVOID *) &sl );
3590 if (FAILED( res ))
3592 ERR("CLSID_ShellLink not available\n");
3593 goto err;
3596 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3597 if (FAILED( res ))
3599 ERR("QueryInterface(IID_IPersistFile) failed\n");
3600 goto err;
3603 target = MSI_RecordGetString(row, 5);
3604 if (strchrW(target, '['))
3606 deformat_string( package, target, &path );
3607 TRACE("target path is %s\n", debugstr_w(path));
3608 IShellLinkW_SetPath( sl, path );
3609 msi_free( path );
3611 else
3613 FIXME("poorly handled shortcut format, advertised shortcut\n");
3614 IShellLinkW_SetPath(sl,comp->FullKeypath);
3617 if (!MSI_RecordIsNull(row,6))
3619 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3620 deformat_string(package, arguments, &deformated);
3621 IShellLinkW_SetArguments(sl,deformated);
3622 msi_free(deformated);
3625 if (!MSI_RecordIsNull(row,7))
3627 LPCWSTR description = MSI_RecordGetString(row, 7);
3628 IShellLinkW_SetDescription(sl, description);
3631 if (!MSI_RecordIsNull(row,8))
3632 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3634 if (!MSI_RecordIsNull(row,9))
3636 INT index;
3637 LPCWSTR icon = MSI_RecordGetString(row, 9);
3639 path = msi_build_icon_path(package, icon);
3640 index = MSI_RecordGetInteger(row,10);
3642 /* no value means 0 */
3643 if (index == MSI_NULL_INTEGER)
3644 index = 0;
3646 IShellLinkW_SetIconLocation(sl, path, index);
3647 msi_free(path);
3650 if (!MSI_RecordIsNull(row,11))
3651 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3653 if (!MSI_RecordIsNull(row,12))
3655 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3656 full_path = msi_get_target_folder( package, wkdir );
3657 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3659 link_file = get_link_file(package, row);
3661 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3662 IPersistFile_Save(pf, link_file, FALSE);
3663 msi_free(link_file);
3665 err:
3666 if (pf)
3667 IPersistFile_Release( pf );
3668 if (sl)
3669 IShellLinkW_Release( sl );
3671 return ERROR_SUCCESS;
3674 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3676 static const WCHAR query[] = {
3677 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3678 '`','S','h','o','r','t','c','u','t','`',0};
3679 MSIQUERY *view;
3680 HRESULT res;
3681 UINT rc;
3683 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3684 if (rc != ERROR_SUCCESS)
3685 return ERROR_SUCCESS;
3687 res = CoInitialize( NULL );
3689 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3690 msiobj_release(&view->hdr);
3692 if (SUCCEEDED(res)) CoUninitialize();
3693 return rc;
3696 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3698 MSIPACKAGE *package = param;
3699 LPWSTR link_file;
3700 LPCWSTR component;
3701 MSICOMPONENT *comp;
3703 component = MSI_RecordGetString( row, 4 );
3704 comp = msi_get_loaded_component( package, component );
3705 if (!comp)
3706 return ERROR_SUCCESS;
3708 comp->Action = msi_get_component_action( package, comp );
3709 if (comp->Action != INSTALLSTATE_ABSENT)
3711 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3712 return ERROR_SUCCESS;
3714 msi_ui_actiondata( package, szRemoveShortcuts, row );
3716 link_file = get_link_file( package, row );
3718 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3719 if (!DeleteFileW( link_file ))
3721 WARN("Failed to remove shortcut file %u\n", GetLastError());
3723 msi_free( link_file );
3725 return ERROR_SUCCESS;
3728 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3730 static const WCHAR query[] = {
3731 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3732 '`','S','h','o','r','t','c','u','t','`',0};
3733 MSIQUERY *view;
3734 UINT rc;
3736 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3737 if (rc != ERROR_SUCCESS)
3738 return ERROR_SUCCESS;
3740 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3741 msiobj_release( &view->hdr );
3742 return rc;
3745 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3747 MSIPACKAGE* package = param;
3748 HANDLE the_file;
3749 LPWSTR FilePath;
3750 LPCWSTR FileName;
3751 CHAR buffer[1024];
3752 DWORD sz;
3753 UINT rc;
3755 FileName = MSI_RecordGetString(row,1);
3756 if (!FileName)
3758 ERR("Unable to get FileName\n");
3759 return ERROR_SUCCESS;
3762 FilePath = msi_build_icon_path(package, FileName);
3764 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3766 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3767 FILE_ATTRIBUTE_NORMAL, NULL);
3769 if (the_file == INVALID_HANDLE_VALUE)
3771 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3772 msi_free(FilePath);
3773 return ERROR_SUCCESS;
3778 DWORD write;
3779 sz = 1024;
3780 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3781 if (rc != ERROR_SUCCESS)
3783 ERR("Failed to get stream\n");
3784 CloseHandle(the_file);
3785 DeleteFileW(FilePath);
3786 break;
3788 WriteFile(the_file,buffer,sz,&write,NULL);
3789 } while (sz == 1024);
3791 msi_free(FilePath);
3792 CloseHandle(the_file);
3794 return ERROR_SUCCESS;
3797 static UINT msi_publish_icons(MSIPACKAGE *package)
3799 static const WCHAR query[]= {
3800 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3801 '`','I','c','o','n','`',0};
3802 MSIQUERY *view;
3803 UINT r;
3805 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3806 if (r == ERROR_SUCCESS)
3808 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3809 msiobj_release(&view->hdr);
3810 if (r != ERROR_SUCCESS)
3811 return r;
3813 return ERROR_SUCCESS;
3816 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3818 UINT r;
3819 HKEY source;
3820 LPWSTR buffer;
3821 MSIMEDIADISK *disk;
3822 MSISOURCELISTINFO *info;
3824 r = RegCreateKeyW(hkey, szSourceList, &source);
3825 if (r != ERROR_SUCCESS)
3826 return r;
3828 RegCloseKey(source);
3830 buffer = strrchrW(package->PackagePath, '\\') + 1;
3831 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3832 package->Context, MSICODE_PRODUCT,
3833 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3834 if (r != ERROR_SUCCESS)
3835 return r;
3837 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3838 package->Context, MSICODE_PRODUCT,
3839 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3840 if (r != ERROR_SUCCESS)
3841 return r;
3843 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3844 package->Context, MSICODE_PRODUCT,
3845 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3846 if (r != ERROR_SUCCESS)
3847 return r;
3849 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3851 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3852 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3853 info->options, info->value);
3854 else
3855 MsiSourceListSetInfoW(package->ProductCode, NULL,
3856 info->context, info->options,
3857 info->property, info->value);
3860 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3862 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3863 disk->context, disk->options,
3864 disk->disk_id, disk->volume_label, disk->disk_prompt);
3867 return ERROR_SUCCESS;
3870 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3872 MSIHANDLE hdb, suminfo;
3873 WCHAR guids[MAX_PATH];
3874 WCHAR packcode[SQUISH_GUID_SIZE];
3875 LPWSTR buffer;
3876 LPWSTR ptr;
3877 DWORD langid;
3878 DWORD size;
3879 UINT r;
3881 static const WCHAR szARPProductIcon[] =
3882 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3883 static const WCHAR szAssignment[] =
3884 {'A','s','s','i','g','n','m','e','n','t',0};
3885 static const WCHAR szAdvertiseFlags[] =
3886 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3887 static const WCHAR szClients[] =
3888 {'C','l','i','e','n','t','s',0};
3889 static const WCHAR szColon[] = {':',0};
3891 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3892 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3893 msi_free(buffer);
3895 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3896 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3898 /* FIXME */
3899 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3901 buffer = msi_dup_property(package->db, szARPProductIcon);
3902 if (buffer)
3904 LPWSTR path = msi_build_icon_path(package, buffer);
3905 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3906 msi_free(path);
3907 msi_free(buffer);
3910 buffer = msi_dup_property(package->db, szProductVersion);
3911 if (buffer)
3913 DWORD verdword = msi_version_str_to_dword(buffer);
3914 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3915 msi_free(buffer);
3918 msi_reg_set_val_dword(hkey, szAssignment, 0);
3919 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3920 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3921 msi_reg_set_val_str(hkey, szClients, szColon);
3923 hdb = alloc_msihandle(&package->db->hdr);
3924 if (!hdb)
3925 return ERROR_NOT_ENOUGH_MEMORY;
3927 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3928 MsiCloseHandle(hdb);
3929 if (r != ERROR_SUCCESS)
3930 goto done;
3932 size = MAX_PATH;
3933 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3934 NULL, guids, &size);
3935 if (r != ERROR_SUCCESS)
3936 goto done;
3938 ptr = strchrW(guids, ';');
3939 if (ptr) *ptr = 0;
3940 squash_guid(guids, packcode);
3941 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3943 done:
3944 MsiCloseHandle(suminfo);
3945 return ERROR_SUCCESS;
3948 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3950 UINT r;
3951 HKEY hkey;
3952 LPWSTR upgrade;
3953 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3955 upgrade = msi_dup_property(package->db, szUpgradeCode);
3956 if (!upgrade)
3957 return ERROR_SUCCESS;
3959 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3960 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3961 else
3962 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3964 if (r != ERROR_SUCCESS)
3966 WARN("failed to open upgrade code key\n");
3967 msi_free(upgrade);
3968 return ERROR_SUCCESS;
3970 squash_guid(package->ProductCode, squashed_pc);
3971 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3972 RegCloseKey(hkey);
3973 msi_free(upgrade);
3974 return ERROR_SUCCESS;
3977 static BOOL msi_check_publish(MSIPACKAGE *package)
3979 MSIFEATURE *feature;
3981 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3983 feature->Action = msi_get_feature_action( package, feature );
3984 if (feature->Action == INSTALLSTATE_LOCAL)
3985 return TRUE;
3988 return FALSE;
3991 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3993 MSIFEATURE *feature;
3995 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3997 feature->Action = msi_get_feature_action( package, feature );
3998 if (feature->Action != INSTALLSTATE_ABSENT)
3999 return FALSE;
4002 return TRUE;
4005 static UINT msi_publish_patches( MSIPACKAGE *package )
4007 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4008 WCHAR patch_squashed[GUID_SIZE];
4009 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4010 LONG res;
4011 MSIPATCHINFO *patch;
4012 UINT r;
4013 WCHAR *p, *all_patches = NULL;
4014 DWORD len = 0;
4016 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4017 if (r != ERROR_SUCCESS)
4018 return ERROR_FUNCTION_FAILED;
4020 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4021 if (res != ERROR_SUCCESS)
4023 r = ERROR_FUNCTION_FAILED;
4024 goto done;
4027 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4028 if (r != ERROR_SUCCESS)
4029 goto done;
4031 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4033 squash_guid( patch->patchcode, patch_squashed );
4034 len += strlenW( patch_squashed ) + 1;
4037 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4038 if (!all_patches)
4039 goto done;
4041 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4043 HKEY patch_key;
4045 squash_guid( patch->patchcode, p );
4046 p += strlenW( p ) + 1;
4048 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4049 (const BYTE *)patch->transforms,
4050 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4051 if (res != ERROR_SUCCESS)
4052 goto done;
4054 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4055 if (r != ERROR_SUCCESS)
4056 goto done;
4058 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4059 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4060 RegCloseKey( patch_key );
4061 if (res != ERROR_SUCCESS)
4062 goto done;
4064 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4066 res = GetLastError();
4067 ERR("Unable to copy patch package %d\n", res);
4068 goto done;
4070 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4071 if (res != ERROR_SUCCESS)
4072 goto done;
4074 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4075 RegCloseKey( patch_key );
4076 if (res != ERROR_SUCCESS)
4077 goto done;
4080 all_patches[len] = 0;
4081 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4082 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4083 if (res != ERROR_SUCCESS)
4084 goto done;
4086 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4087 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4088 if (res != ERROR_SUCCESS)
4089 r = ERROR_FUNCTION_FAILED;
4091 done:
4092 RegCloseKey( product_patches_key );
4093 RegCloseKey( patches_key );
4094 RegCloseKey( product_key );
4095 msi_free( all_patches );
4096 return r;
4099 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4101 UINT rc;
4102 HKEY hukey = NULL, hudkey = NULL;
4103 MSIRECORD *uirow;
4105 if (!list_empty(&package->patches))
4107 rc = msi_publish_patches(package);
4108 if (rc != ERROR_SUCCESS)
4109 goto end;
4112 /* FIXME: also need to publish if the product is in advertise mode */
4113 if (!msi_check_publish(package))
4114 return ERROR_SUCCESS;
4116 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4117 &hukey, TRUE);
4118 if (rc != ERROR_SUCCESS)
4119 goto end;
4121 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4122 NULL, &hudkey, TRUE);
4123 if (rc != ERROR_SUCCESS)
4124 goto end;
4126 rc = msi_publish_upgrade_code(package);
4127 if (rc != ERROR_SUCCESS)
4128 goto end;
4130 rc = msi_publish_product_properties(package, hukey);
4131 if (rc != ERROR_SUCCESS)
4132 goto end;
4134 rc = msi_publish_sourcelist(package, hukey);
4135 if (rc != ERROR_SUCCESS)
4136 goto end;
4138 rc = msi_publish_icons(package);
4140 end:
4141 uirow = MSI_CreateRecord( 1 );
4142 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4143 msi_ui_actiondata( package, szPublishProduct, uirow );
4144 msiobj_release( &uirow->hdr );
4146 RegCloseKey(hukey);
4147 RegCloseKey(hudkey);
4148 return rc;
4151 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4153 WCHAR *filename, *ptr, *folder, *ret;
4154 const WCHAR *dirprop;
4156 filename = msi_dup_record_field( row, 2 );
4157 if (filename && (ptr = strchrW( filename, '|' )))
4158 ptr++;
4159 else
4160 ptr = filename;
4162 dirprop = MSI_RecordGetString( row, 3 );
4163 if (dirprop)
4165 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4166 if (!folder) folder = msi_dup_property( package->db, dirprop );
4168 else
4169 folder = msi_dup_property( package->db, szWindowsFolder );
4171 if (!folder)
4173 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4174 msi_free( filename );
4175 return NULL;
4178 ret = msi_build_directory_name( 2, folder, ptr );
4180 msi_free( filename );
4181 msi_free( folder );
4182 return ret;
4185 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4187 MSIPACKAGE *package = param;
4188 LPCWSTR component, section, key, value, identifier;
4189 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4190 MSIRECORD * uirow;
4191 INT action;
4192 MSICOMPONENT *comp;
4194 component = MSI_RecordGetString(row, 8);
4195 comp = msi_get_loaded_component(package,component);
4196 if (!comp)
4197 return ERROR_SUCCESS;
4199 comp->Action = msi_get_component_action( package, comp );
4200 if (comp->Action != INSTALLSTATE_LOCAL)
4202 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4203 return ERROR_SUCCESS;
4206 identifier = MSI_RecordGetString(row,1);
4207 section = MSI_RecordGetString(row,4);
4208 key = MSI_RecordGetString(row,5);
4209 value = MSI_RecordGetString(row,6);
4210 action = MSI_RecordGetInteger(row,7);
4212 deformat_string(package,section,&deformated_section);
4213 deformat_string(package,key,&deformated_key);
4214 deformat_string(package,value,&deformated_value);
4216 fullname = get_ini_file_name(package, row);
4218 if (action == 0)
4220 TRACE("Adding value %s to section %s in %s\n",
4221 debugstr_w(deformated_key), debugstr_w(deformated_section),
4222 debugstr_w(fullname));
4223 WritePrivateProfileStringW(deformated_section, deformated_key,
4224 deformated_value, fullname);
4226 else if (action == 1)
4228 WCHAR returned[10];
4229 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4230 returned, 10, fullname);
4231 if (returned[0] == 0)
4233 TRACE("Adding value %s to section %s in %s\n",
4234 debugstr_w(deformated_key), debugstr_w(deformated_section),
4235 debugstr_w(fullname));
4237 WritePrivateProfileStringW(deformated_section, deformated_key,
4238 deformated_value, fullname);
4241 else if (action == 3)
4242 FIXME("Append to existing section not yet implemented\n");
4244 uirow = MSI_CreateRecord(4);
4245 MSI_RecordSetStringW(uirow,1,identifier);
4246 MSI_RecordSetStringW(uirow,2,deformated_section);
4247 MSI_RecordSetStringW(uirow,3,deformated_key);
4248 MSI_RecordSetStringW(uirow,4,deformated_value);
4249 msi_ui_actiondata( package, szWriteIniValues, uirow );
4250 msiobj_release( &uirow->hdr );
4252 msi_free(fullname);
4253 msi_free(deformated_key);
4254 msi_free(deformated_value);
4255 msi_free(deformated_section);
4256 return ERROR_SUCCESS;
4259 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4261 static const WCHAR query[] = {
4262 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4263 '`','I','n','i','F','i','l','e','`',0};
4264 MSIQUERY *view;
4265 UINT rc;
4267 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4268 if (rc != ERROR_SUCCESS)
4269 return ERROR_SUCCESS;
4271 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4272 msiobj_release(&view->hdr);
4273 return rc;
4276 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4278 MSIPACKAGE *package = param;
4279 LPCWSTR component, section, key, value, identifier;
4280 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4281 MSICOMPONENT *comp;
4282 MSIRECORD *uirow;
4283 INT action;
4285 component = MSI_RecordGetString( row, 8 );
4286 comp = msi_get_loaded_component( package, component );
4287 if (!comp)
4288 return ERROR_SUCCESS;
4290 comp->Action = msi_get_component_action( package, comp );
4291 if (comp->Action != INSTALLSTATE_ABSENT)
4293 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4294 return ERROR_SUCCESS;
4297 identifier = MSI_RecordGetString( row, 1 );
4298 section = MSI_RecordGetString( row, 4 );
4299 key = MSI_RecordGetString( row, 5 );
4300 value = MSI_RecordGetString( row, 6 );
4301 action = MSI_RecordGetInteger( row, 7 );
4303 deformat_string( package, section, &deformated_section );
4304 deformat_string( package, key, &deformated_key );
4305 deformat_string( package, value, &deformated_value );
4307 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4309 filename = get_ini_file_name( package, row );
4311 TRACE("Removing key %s from section %s in %s\n",
4312 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4314 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4316 WARN("Unable to remove key %u\n", GetLastError());
4318 msi_free( filename );
4320 else
4321 FIXME("Unsupported action %d\n", action);
4324 uirow = MSI_CreateRecord( 4 );
4325 MSI_RecordSetStringW( uirow, 1, identifier );
4326 MSI_RecordSetStringW( uirow, 2, deformated_section );
4327 MSI_RecordSetStringW( uirow, 3, deformated_key );
4328 MSI_RecordSetStringW( uirow, 4, deformated_value );
4329 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4330 msiobj_release( &uirow->hdr );
4332 msi_free( deformated_key );
4333 msi_free( deformated_value );
4334 msi_free( deformated_section );
4335 return ERROR_SUCCESS;
4338 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4340 MSIPACKAGE *package = param;
4341 LPCWSTR component, section, key, value, identifier;
4342 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4343 MSICOMPONENT *comp;
4344 MSIRECORD *uirow;
4345 INT action;
4347 component = MSI_RecordGetString( row, 8 );
4348 comp = msi_get_loaded_component( package, component );
4349 if (!comp)
4350 return ERROR_SUCCESS;
4352 comp->Action = msi_get_component_action( package, comp );
4353 if (comp->Action != INSTALLSTATE_LOCAL)
4355 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4356 return ERROR_SUCCESS;
4359 identifier = MSI_RecordGetString( row, 1 );
4360 section = MSI_RecordGetString( row, 4 );
4361 key = MSI_RecordGetString( row, 5 );
4362 value = MSI_RecordGetString( row, 6 );
4363 action = MSI_RecordGetInteger( row, 7 );
4365 deformat_string( package, section, &deformated_section );
4366 deformat_string( package, key, &deformated_key );
4367 deformat_string( package, value, &deformated_value );
4369 if (action == msidbIniFileActionRemoveLine)
4371 filename = get_ini_file_name( package, row );
4373 TRACE("Removing key %s from section %s in %s\n",
4374 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4376 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4378 WARN("Unable to remove key %u\n", GetLastError());
4380 msi_free( filename );
4382 else
4383 FIXME("Unsupported action %d\n", action);
4385 uirow = MSI_CreateRecord( 4 );
4386 MSI_RecordSetStringW( uirow, 1, identifier );
4387 MSI_RecordSetStringW( uirow, 2, deformated_section );
4388 MSI_RecordSetStringW( uirow, 3, deformated_key );
4389 MSI_RecordSetStringW( uirow, 4, deformated_value );
4390 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4391 msiobj_release( &uirow->hdr );
4393 msi_free( deformated_key );
4394 msi_free( deformated_value );
4395 msi_free( deformated_section );
4396 return ERROR_SUCCESS;
4399 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4401 static const WCHAR query[] = {
4402 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4403 '`','I','n','i','F','i','l','e','`',0};
4404 static const WCHAR remove_query[] = {
4405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4406 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4407 MSIQUERY *view;
4408 UINT rc;
4410 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4411 if (rc == ERROR_SUCCESS)
4413 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4414 msiobj_release( &view->hdr );
4415 if (rc != ERROR_SUCCESS)
4416 return rc;
4418 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4419 if (rc == ERROR_SUCCESS)
4421 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4422 msiobj_release( &view->hdr );
4423 if (rc != ERROR_SUCCESS)
4424 return rc;
4426 return ERROR_SUCCESS;
4429 static void register_dll( const WCHAR *dll, BOOL unregister )
4431 HMODULE hmod;
4433 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4434 if (hmod)
4436 HRESULT (WINAPI *func_ptr)( void );
4437 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4439 func_ptr = (void *)GetProcAddress( hmod, func );
4440 if (func_ptr)
4442 HRESULT hr = func_ptr();
4443 if (FAILED( hr ))
4444 WARN("failed to register dll 0x%08x\n", hr);
4446 else
4447 WARN("entry point %s not found\n", func);
4448 FreeLibrary( hmod );
4449 return;
4451 WARN("failed to load library %u\n", GetLastError());
4454 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4456 MSIPACKAGE *package = param;
4457 LPCWSTR filename;
4458 MSIFILE *file;
4459 MSIRECORD *uirow;
4461 filename = MSI_RecordGetString( row, 1 );
4462 file = msi_get_loaded_file( package, filename );
4463 if (!file)
4465 WARN("unable to find file %s\n", debugstr_w(filename));
4466 return ERROR_SUCCESS;
4468 file->Component->Action = msi_get_component_action( package, file->Component );
4469 if (file->Component->Action != INSTALLSTATE_LOCAL)
4471 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4472 return ERROR_SUCCESS;
4475 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4476 register_dll( file->TargetPath, FALSE );
4478 uirow = MSI_CreateRecord( 2 );
4479 MSI_RecordSetStringW( uirow, 1, file->File );
4480 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4481 msi_ui_actiondata( package, szSelfRegModules, uirow );
4482 msiobj_release( &uirow->hdr );
4484 return ERROR_SUCCESS;
4487 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4489 static const WCHAR query[] = {
4490 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4491 '`','S','e','l','f','R','e','g','`',0};
4492 MSIQUERY *view;
4493 UINT rc;
4495 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4496 if (rc != ERROR_SUCCESS)
4497 return ERROR_SUCCESS;
4499 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4500 msiobj_release(&view->hdr);
4501 return rc;
4504 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4506 MSIPACKAGE *package = param;
4507 LPCWSTR filename;
4508 MSIFILE *file;
4509 MSIRECORD *uirow;
4511 filename = MSI_RecordGetString( row, 1 );
4512 file = msi_get_loaded_file( package, filename );
4513 if (!file)
4515 WARN("unable to find file %s\n", debugstr_w(filename));
4516 return ERROR_SUCCESS;
4518 file->Component->Action = msi_get_component_action( package, file->Component );
4519 if (file->Component->Action != INSTALLSTATE_ABSENT)
4521 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4522 return ERROR_SUCCESS;
4525 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4526 register_dll( file->TargetPath, TRUE );
4528 uirow = MSI_CreateRecord( 2 );
4529 MSI_RecordSetStringW( uirow, 1, file->File );
4530 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4531 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4532 msiobj_release( &uirow->hdr );
4534 return ERROR_SUCCESS;
4537 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4539 static const WCHAR query[] = {
4540 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4541 '`','S','e','l','f','R','e','g','`',0};
4542 MSIQUERY *view;
4543 UINT rc;
4545 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4546 if (rc != ERROR_SUCCESS)
4547 return ERROR_SUCCESS;
4549 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4550 msiobj_release( &view->hdr );
4551 return rc;
4554 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4556 MSIFEATURE *feature;
4557 UINT rc;
4558 HKEY hkey = NULL, userdata = NULL;
4560 if (!msi_check_publish(package))
4561 return ERROR_SUCCESS;
4563 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4564 &hkey, TRUE);
4565 if (rc != ERROR_SUCCESS)
4566 goto end;
4568 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4569 &userdata, TRUE);
4570 if (rc != ERROR_SUCCESS)
4571 goto end;
4573 /* here the guids are base 85 encoded */
4574 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4576 ComponentList *cl;
4577 LPWSTR data = NULL;
4578 GUID clsid;
4579 INT size;
4580 BOOL absent = FALSE;
4581 MSIRECORD *uirow;
4583 if (feature->Action != INSTALLSTATE_LOCAL &&
4584 feature->Action != INSTALLSTATE_SOURCE &&
4585 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4587 size = 1;
4588 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4590 size += 21;
4592 if (feature->Feature_Parent)
4593 size += strlenW( feature->Feature_Parent )+2;
4595 data = msi_alloc(size * sizeof(WCHAR));
4597 data[0] = 0;
4598 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4600 MSICOMPONENT* component = cl->component;
4601 WCHAR buf[21];
4603 buf[0] = 0;
4604 if (component->ComponentId)
4606 TRACE("From %s\n",debugstr_w(component->ComponentId));
4607 CLSIDFromString(component->ComponentId, &clsid);
4608 encode_base85_guid(&clsid,buf);
4609 TRACE("to %s\n",debugstr_w(buf));
4610 strcatW(data,buf);
4614 if (feature->Feature_Parent)
4616 static const WCHAR sep[] = {'\2',0};
4617 strcatW(data,sep);
4618 strcatW(data,feature->Feature_Parent);
4621 msi_reg_set_val_str( userdata, feature->Feature, data );
4622 msi_free(data);
4624 size = 0;
4625 if (feature->Feature_Parent)
4626 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4627 if (!absent)
4629 size += sizeof(WCHAR);
4630 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4631 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4633 else
4635 size += 2*sizeof(WCHAR);
4636 data = msi_alloc(size);
4637 data[0] = 0x6;
4638 data[1] = 0;
4639 if (feature->Feature_Parent)
4640 strcpyW( &data[1], feature->Feature_Parent );
4641 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4642 (LPBYTE)data,size);
4643 msi_free(data);
4646 /* the UI chunk */
4647 uirow = MSI_CreateRecord( 1 );
4648 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4649 msi_ui_actiondata( package, szPublishFeatures, uirow );
4650 msiobj_release( &uirow->hdr );
4651 /* FIXME: call msi_ui_progress? */
4654 end:
4655 RegCloseKey(hkey);
4656 RegCloseKey(userdata);
4657 return rc;
4660 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4662 UINT r;
4663 HKEY hkey;
4664 MSIRECORD *uirow;
4666 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4668 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4669 &hkey, FALSE);
4670 if (r == ERROR_SUCCESS)
4672 RegDeleteValueW(hkey, feature->Feature);
4673 RegCloseKey(hkey);
4676 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4677 &hkey, FALSE);
4678 if (r == ERROR_SUCCESS)
4680 RegDeleteValueW(hkey, feature->Feature);
4681 RegCloseKey(hkey);
4684 uirow = MSI_CreateRecord( 1 );
4685 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4686 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4687 msiobj_release( &uirow->hdr );
4689 return ERROR_SUCCESS;
4692 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4694 MSIFEATURE *feature;
4696 if (!msi_check_unpublish(package))
4697 return ERROR_SUCCESS;
4699 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4701 msi_unpublish_feature(package, feature);
4704 return ERROR_SUCCESS;
4707 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4709 SYSTEMTIME systime;
4710 DWORD size, langid;
4711 WCHAR date[9], *val, *buffer;
4712 const WCHAR *prop, *key;
4714 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4715 static const WCHAR modpath_fmt[] =
4716 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4717 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4718 static const WCHAR szModifyPath[] =
4719 {'M','o','d','i','f','y','P','a','t','h',0};
4720 static const WCHAR szUninstallString[] =
4721 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4722 static const WCHAR szEstimatedSize[] =
4723 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4724 static const WCHAR szDisplayVersion[] =
4725 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4726 static const WCHAR szInstallSource[] =
4727 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4728 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4729 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4730 static const WCHAR szAuthorizedCDFPrefix[] =
4731 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4732 static const WCHAR szARPCONTACT[] =
4733 {'A','R','P','C','O','N','T','A','C','T',0};
4734 static const WCHAR szContact[] =
4735 {'C','o','n','t','a','c','t',0};
4736 static const WCHAR szARPCOMMENTS[] =
4737 {'A','R','P','C','O','M','M','E','N','T','S',0};
4738 static const WCHAR szComments[] =
4739 {'C','o','m','m','e','n','t','s',0};
4740 static const WCHAR szProductName[] =
4741 {'P','r','o','d','u','c','t','N','a','m','e',0};
4742 static const WCHAR szDisplayName[] =
4743 {'D','i','s','p','l','a','y','N','a','m','e',0};
4744 static const WCHAR szARPHELPLINK[] =
4745 {'A','R','P','H','E','L','P','L','I','N','K',0};
4746 static const WCHAR szHelpLink[] =
4747 {'H','e','l','p','L','i','n','k',0};
4748 static const WCHAR szARPHELPTELEPHONE[] =
4749 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4750 static const WCHAR szHelpTelephone[] =
4751 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4752 static const WCHAR szARPINSTALLLOCATION[] =
4753 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4754 static const WCHAR szInstallLocation[] =
4755 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4756 static const WCHAR szManufacturer[] =
4757 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4758 static const WCHAR szPublisher[] =
4759 {'P','u','b','l','i','s','h','e','r',0};
4760 static const WCHAR szARPREADME[] =
4761 {'A','R','P','R','E','A','D','M','E',0};
4762 static const WCHAR szReadme[] =
4763 {'R','e','a','d','M','e',0};
4764 static const WCHAR szARPSIZE[] =
4765 {'A','R','P','S','I','Z','E',0};
4766 static const WCHAR szSize[] =
4767 {'S','i','z','e',0};
4768 static const WCHAR szARPURLINFOABOUT[] =
4769 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4770 static const WCHAR szURLInfoAbout[] =
4771 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4772 static const WCHAR szARPURLUPDATEINFO[] =
4773 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4774 static const WCHAR szURLUpdateInfo[] =
4775 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4776 static const WCHAR szARPSYSTEMCOMPONENT[] =
4777 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4778 static const WCHAR szSystemComponent[] =
4779 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4781 static const WCHAR *propval[] = {
4782 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4783 szARPCONTACT, szContact,
4784 szARPCOMMENTS, szComments,
4785 szProductName, szDisplayName,
4786 szARPHELPLINK, szHelpLink,
4787 szARPHELPTELEPHONE, szHelpTelephone,
4788 szARPINSTALLLOCATION, szInstallLocation,
4789 szSourceDir, szInstallSource,
4790 szManufacturer, szPublisher,
4791 szARPREADME, szReadme,
4792 szARPSIZE, szSize,
4793 szARPURLINFOABOUT, szURLInfoAbout,
4794 szARPURLUPDATEINFO, szURLUpdateInfo,
4795 NULL
4797 const WCHAR **p = propval;
4799 while (*p)
4801 prop = *p++;
4802 key = *p++;
4803 val = msi_dup_property(package->db, prop);
4804 msi_reg_set_val_str(hkey, key, val);
4805 msi_free(val);
4808 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4809 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4811 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4813 size = deformat_string(package, modpath_fmt, &buffer);
4814 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4815 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4816 msi_free(buffer);
4818 /* FIXME: Write real Estimated Size when we have it */
4819 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4821 GetLocalTime(&systime);
4822 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4823 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4825 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4826 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4828 buffer = msi_dup_property(package->db, szProductVersion);
4829 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4830 if (buffer)
4832 DWORD verdword = msi_version_str_to_dword(buffer);
4834 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4835 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4836 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4837 msi_free(buffer);
4840 return ERROR_SUCCESS;
4843 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4845 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4846 MSIRECORD *uirow;
4847 LPWSTR upgrade_code;
4848 HKEY hkey, props, upgrade_key;
4849 UINT rc;
4851 /* FIXME: also need to publish if the product is in advertise mode */
4852 if (!msi_check_publish(package))
4853 return ERROR_SUCCESS;
4855 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4856 if (rc != ERROR_SUCCESS)
4857 return rc;
4859 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4860 if (rc != ERROR_SUCCESS)
4861 goto done;
4863 rc = msi_publish_install_properties(package, hkey);
4864 if (rc != ERROR_SUCCESS)
4865 goto done;
4867 rc = msi_publish_install_properties(package, props);
4868 if (rc != ERROR_SUCCESS)
4869 goto done;
4871 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4872 if (upgrade_code)
4874 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4875 if (rc == ERROR_SUCCESS)
4877 squash_guid( package->ProductCode, squashed_pc );
4878 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4879 RegCloseKey( upgrade_key );
4881 msi_free( upgrade_code );
4883 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4884 package->delete_on_close = FALSE;
4886 done:
4887 uirow = MSI_CreateRecord( 1 );
4888 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4889 msi_ui_actiondata( package, szRegisterProduct, uirow );
4890 msiobj_release( &uirow->hdr );
4892 RegCloseKey(hkey);
4893 return ERROR_SUCCESS;
4896 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4898 return execute_script(package, SCRIPT_INSTALL);
4901 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4903 MSIPACKAGE *package = param;
4904 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4905 WCHAR *p, *icon_path;
4907 if (!icon) return ERROR_SUCCESS;
4908 if ((icon_path = msi_build_icon_path( package, icon )))
4910 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4911 DeleteFileW( icon_path );
4912 if ((p = strrchrW( icon_path, '\\' )))
4914 *p = 0;
4915 RemoveDirectoryW( icon_path );
4917 msi_free( icon_path );
4919 return ERROR_SUCCESS;
4922 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4924 static const WCHAR query[]= {
4925 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4926 MSIQUERY *view;
4927 UINT r;
4929 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4930 if (r == ERROR_SUCCESS)
4932 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4933 msiobj_release( &view->hdr );
4934 if (r != ERROR_SUCCESS)
4935 return r;
4937 return ERROR_SUCCESS;
4940 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4942 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4943 WCHAR *upgrade, **features;
4944 BOOL full_uninstall = TRUE;
4945 MSIFEATURE *feature;
4946 MSIPATCHINFO *patch;
4947 UINT i;
4949 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4951 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4953 features = msi_split_string( remove, ',' );
4954 for (i = 0; features && features[i]; i++)
4956 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4958 msi_free(features);
4960 if (!full_uninstall)
4961 return ERROR_SUCCESS;
4963 MSIREG_DeleteProductKey(package->ProductCode);
4964 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4965 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4967 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4968 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4969 MSIREG_DeleteUserProductKey(package->ProductCode);
4970 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4972 upgrade = msi_dup_property(package->db, szUpgradeCode);
4973 if (upgrade)
4975 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4976 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4977 msi_free(upgrade);
4980 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4982 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4983 if (!strcmpW( package->ProductCode, patch->products ))
4985 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
4986 patch->delete_on_close = TRUE;
4988 /* FIXME: remove local patch package if this is the last product */
4990 TRACE("removing local package %s\n", debugstr_w(package->localfile));
4991 package->delete_on_close = TRUE;
4993 msi_unpublish_icons( package );
4994 return ERROR_SUCCESS;
4997 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4999 UINT rc;
5000 WCHAR *remove;
5002 /* turn off scheduling */
5003 package->script->CurrentlyScripting= FALSE;
5005 /* first do the same as an InstallExecute */
5006 rc = ACTION_InstallExecute(package);
5007 if (rc != ERROR_SUCCESS)
5008 return rc;
5010 /* then handle commit actions */
5011 rc = execute_script(package, SCRIPT_COMMIT);
5012 if (rc != ERROR_SUCCESS)
5013 return rc;
5015 remove = msi_dup_property(package->db, szRemove);
5016 rc = msi_unpublish_product(package, remove);
5017 msi_free(remove);
5018 return rc;
5021 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5023 static const WCHAR RunOnce[] = {
5024 'S','o','f','t','w','a','r','e','\\',
5025 'M','i','c','r','o','s','o','f','t','\\',
5026 'W','i','n','d','o','w','s','\\',
5027 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5028 'R','u','n','O','n','c','e',0};
5029 static const WCHAR InstallRunOnce[] = {
5030 'S','o','f','t','w','a','r','e','\\',
5031 'M','i','c','r','o','s','o','f','t','\\',
5032 'W','i','n','d','o','w','s','\\',
5033 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5034 'I','n','s','t','a','l','l','e','r','\\',
5035 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5037 static const WCHAR msiexec_fmt[] = {
5038 '%','s',
5039 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5040 '\"','%','s','\"',0};
5041 static const WCHAR install_fmt[] = {
5042 '/','I',' ','\"','%','s','\"',' ',
5043 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5044 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5045 WCHAR buffer[256], sysdir[MAX_PATH];
5046 HKEY hkey;
5047 WCHAR squished_pc[100];
5049 squash_guid(package->ProductCode,squished_pc);
5051 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5052 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5053 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5054 squished_pc);
5056 msi_reg_set_val_str( hkey, squished_pc, buffer );
5057 RegCloseKey(hkey);
5059 TRACE("Reboot command %s\n",debugstr_w(buffer));
5061 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5062 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5064 msi_reg_set_val_str( hkey, squished_pc, buffer );
5065 RegCloseKey(hkey);
5067 return ERROR_INSTALL_SUSPEND;
5070 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5072 static const WCHAR query[] =
5073 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5074 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5075 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5076 MSIRECORD *rec, *row;
5077 DWORD i, size = 0;
5078 va_list va;
5079 const WCHAR *str;
5080 WCHAR *data;
5082 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5084 rec = MSI_CreateRecord( count + 2 );
5085 str = MSI_RecordGetString( row, 1 );
5086 MSI_RecordSetStringW( rec, 0, str );
5087 msiobj_release( &row->hdr );
5088 MSI_RecordSetInteger( rec, 1, error );
5090 va_start( va, count );
5091 for (i = 0; i < count; i++)
5093 str = va_arg( va, const WCHAR *);
5094 MSI_RecordSetStringW( rec, i + 2, str );
5096 va_end( va );
5098 MSI_FormatRecordW( package, rec, NULL, &size );
5099 size++;
5100 data = msi_alloc( size * sizeof(WCHAR) );
5101 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5102 else data[0] = 0;
5103 msiobj_release( &rec->hdr );
5104 return data;
5107 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5109 DWORD attrib;
5110 UINT rc;
5113 * We are currently doing what should be done here in the top level Install
5114 * however for Administrative and uninstalls this step will be needed
5116 if (!package->PackagePath)
5117 return ERROR_SUCCESS;
5119 msi_set_sourcedir_props(package, TRUE);
5121 attrib = GetFileAttributesW(package->db->path);
5122 if (attrib == INVALID_FILE_ATTRIBUTES)
5124 LPWSTR prompt, msg;
5125 DWORD size = 0;
5127 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5128 package->Context, MSICODE_PRODUCT,
5129 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5130 if (rc == ERROR_MORE_DATA)
5132 prompt = msi_alloc(size * sizeof(WCHAR));
5133 MsiSourceListGetInfoW(package->ProductCode, NULL,
5134 package->Context, MSICODE_PRODUCT,
5135 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5137 else
5138 prompt = strdupW(package->db->path);
5140 msg = msi_build_error_string(package, 1302, 1, prompt);
5141 msi_free(prompt);
5142 while(attrib == INVALID_FILE_ATTRIBUTES)
5144 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5145 if (rc == IDCANCEL)
5147 msi_free(msg);
5148 return ERROR_INSTALL_USEREXIT;
5150 attrib = GetFileAttributesW(package->db->path);
5152 msi_free(msg);
5153 rc = ERROR_SUCCESS;
5155 else
5156 return ERROR_SUCCESS;
5158 return rc;
5161 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5163 HKEY hkey = 0;
5164 LPWSTR buffer, productid = NULL;
5165 UINT i, rc = ERROR_SUCCESS;
5166 MSIRECORD *uirow;
5168 static const WCHAR szPropKeys[][80] =
5170 {'P','r','o','d','u','c','t','I','D',0},
5171 {'U','S','E','R','N','A','M','E',0},
5172 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5173 {0},
5176 static const WCHAR szRegKeys[][80] =
5178 {'P','r','o','d','u','c','t','I','D',0},
5179 {'R','e','g','O','w','n','e','r',0},
5180 {'R','e','g','C','o','m','p','a','n','y',0},
5181 {0},
5184 if (msi_check_unpublish(package))
5186 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5187 goto end;
5190 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5191 if (!productid)
5192 goto end;
5194 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5195 NULL, &hkey, TRUE);
5196 if (rc != ERROR_SUCCESS)
5197 goto end;
5199 for( i = 0; szPropKeys[i][0]; i++ )
5201 buffer = msi_dup_property( package->db, szPropKeys[i] );
5202 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5203 msi_free( buffer );
5206 end:
5207 uirow = MSI_CreateRecord( 1 );
5208 MSI_RecordSetStringW( uirow, 1, productid );
5209 msi_ui_actiondata( package, szRegisterUser, uirow );
5210 msiobj_release( &uirow->hdr );
5212 msi_free(productid);
5213 RegCloseKey(hkey);
5214 return rc;
5218 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5220 UINT rc;
5222 package->script->InWhatSequence |= SEQUENCE_EXEC;
5223 rc = ACTION_ProcessExecSequence(package,FALSE);
5224 return rc;
5227 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5229 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5230 WCHAR productid_85[21], component_85[21], *ret;
5231 GUID clsid;
5232 DWORD sz;
5234 /* > is used if there is a component GUID and < if not. */
5236 productid_85[0] = 0;
5237 component_85[0] = 0;
5238 CLSIDFromString( package->ProductCode, &clsid );
5240 encode_base85_guid( &clsid, productid_85 );
5241 if (component)
5243 CLSIDFromString( component->ComponentId, &clsid );
5244 encode_base85_guid( &clsid, component_85 );
5247 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5248 debugstr_w(component_85));
5250 sz = 20 + strlenW( feature ) + 20 + 3;
5251 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5252 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5253 return ret;
5256 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5258 MSIPACKAGE *package = param;
5259 LPCWSTR compgroupid, component, feature, qualifier, text;
5260 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5261 HKEY hkey = NULL;
5262 UINT rc;
5263 MSICOMPONENT *comp;
5264 MSIFEATURE *feat;
5265 DWORD sz;
5266 MSIRECORD *uirow;
5267 int len;
5269 feature = MSI_RecordGetString(rec, 5);
5270 feat = msi_get_loaded_feature(package, feature);
5271 if (!feat)
5272 return ERROR_SUCCESS;
5274 feat->Action = msi_get_feature_action( package, feat );
5275 if (feat->Action != INSTALLSTATE_LOCAL &&
5276 feat->Action != INSTALLSTATE_SOURCE &&
5277 feat->Action != INSTALLSTATE_ADVERTISED)
5279 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5280 return ERROR_SUCCESS;
5283 component = MSI_RecordGetString(rec, 3);
5284 comp = msi_get_loaded_component(package, component);
5285 if (!comp)
5286 return ERROR_SUCCESS;
5288 compgroupid = MSI_RecordGetString(rec,1);
5289 qualifier = MSI_RecordGetString(rec,2);
5291 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5292 if (rc != ERROR_SUCCESS)
5293 goto end;
5295 advertise = msi_create_component_advertise_string( package, comp, feature );
5296 text = MSI_RecordGetString( rec, 4 );
5297 if (text)
5299 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5300 strcpyW( p, advertise );
5301 strcatW( p, text );
5302 msi_free( advertise );
5303 advertise = p;
5305 existing = msi_reg_get_val_str( hkey, qualifier );
5307 sz = strlenW( advertise ) + 1;
5308 if (existing)
5310 for (p = existing; *p; p += len)
5312 len = strlenW( p ) + 1;
5313 if (strcmpW( advertise, p )) sz += len;
5316 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5318 rc = ERROR_OUTOFMEMORY;
5319 goto end;
5321 q = output;
5322 if (existing)
5324 for (p = existing; *p; p += len)
5326 len = strlenW( p ) + 1;
5327 if (strcmpW( advertise, p ))
5329 memcpy( q, p, len * sizeof(WCHAR) );
5330 q += len;
5334 strcpyW( q, advertise );
5335 q[strlenW( q ) + 1] = 0;
5337 msi_reg_set_val_multi_str( hkey, qualifier, output );
5339 end:
5340 RegCloseKey(hkey);
5341 msi_free( output );
5342 msi_free( advertise );
5343 msi_free( existing );
5345 /* the UI chunk */
5346 uirow = MSI_CreateRecord( 2 );
5347 MSI_RecordSetStringW( uirow, 1, compgroupid );
5348 MSI_RecordSetStringW( uirow, 2, qualifier);
5349 msi_ui_actiondata( package, szPublishComponents, uirow );
5350 msiobj_release( &uirow->hdr );
5351 /* FIXME: call ui_progress? */
5353 return rc;
5357 * At present I am ignorning the advertised components part of this and only
5358 * focusing on the qualified component sets
5360 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5362 static const WCHAR query[] = {
5363 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5364 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5365 MSIQUERY *view;
5366 UINT rc;
5368 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5369 if (rc != ERROR_SUCCESS)
5370 return ERROR_SUCCESS;
5372 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5373 msiobj_release(&view->hdr);
5374 return rc;
5377 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5379 static const WCHAR szInstallerComponents[] = {
5380 'S','o','f','t','w','a','r','e','\\',
5381 'M','i','c','r','o','s','o','f','t','\\',
5382 'I','n','s','t','a','l','l','e','r','\\',
5383 'C','o','m','p','o','n','e','n','t','s','\\',0};
5385 MSIPACKAGE *package = param;
5386 LPCWSTR compgroupid, component, feature, qualifier;
5387 MSICOMPONENT *comp;
5388 MSIFEATURE *feat;
5389 MSIRECORD *uirow;
5390 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5391 LONG res;
5393 feature = MSI_RecordGetString( rec, 5 );
5394 feat = msi_get_loaded_feature( package, feature );
5395 if (!feat)
5396 return ERROR_SUCCESS;
5398 feat->Action = msi_get_feature_action( package, feat );
5399 if (feat->Action != INSTALLSTATE_ABSENT)
5401 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5402 return ERROR_SUCCESS;
5405 component = MSI_RecordGetString( rec, 3 );
5406 comp = msi_get_loaded_component( package, component );
5407 if (!comp)
5408 return ERROR_SUCCESS;
5410 compgroupid = MSI_RecordGetString( rec, 1 );
5411 qualifier = MSI_RecordGetString( rec, 2 );
5413 squash_guid( compgroupid, squashed );
5414 strcpyW( keypath, szInstallerComponents );
5415 strcatW( keypath, squashed );
5417 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5418 if (res != ERROR_SUCCESS)
5420 WARN("Unable to delete component key %d\n", res);
5423 uirow = MSI_CreateRecord( 2 );
5424 MSI_RecordSetStringW( uirow, 1, compgroupid );
5425 MSI_RecordSetStringW( uirow, 2, qualifier );
5426 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5427 msiobj_release( &uirow->hdr );
5429 return ERROR_SUCCESS;
5432 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5434 static const WCHAR query[] = {
5435 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5436 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5437 MSIQUERY *view;
5438 UINT rc;
5440 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5441 if (rc != ERROR_SUCCESS)
5442 return ERROR_SUCCESS;
5444 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5445 msiobj_release( &view->hdr );
5446 return rc;
5449 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5451 static const WCHAR query[] =
5452 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5453 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5454 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5455 MSIPACKAGE *package = param;
5456 MSICOMPONENT *component;
5457 MSIRECORD *row;
5458 MSIFILE *file;
5459 SC_HANDLE hscm = NULL, service = NULL;
5460 LPCWSTR comp, key;
5461 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5462 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5463 DWORD serv_type, start_type, err_control;
5464 SERVICE_DESCRIPTIONW sd = {NULL};
5466 comp = MSI_RecordGetString( rec, 12 );
5467 component = msi_get_loaded_component( package, comp );
5468 if (!component)
5470 WARN("service component not found\n");
5471 goto done;
5473 component->Action = msi_get_component_action( package, component );
5474 if (component->Action != INSTALLSTATE_LOCAL)
5476 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5477 goto done;
5479 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5480 if (!hscm)
5482 ERR("Failed to open the SC Manager!\n");
5483 goto done;
5486 start_type = MSI_RecordGetInteger(rec, 5);
5487 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5488 goto done;
5490 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5491 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5492 serv_type = MSI_RecordGetInteger(rec, 4);
5493 err_control = MSI_RecordGetInteger(rec, 6);
5494 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5495 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5496 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5497 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5498 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5499 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5501 /* fetch the service path */
5502 row = MSI_QueryGetRecord(package->db, query, comp);
5503 if (!row)
5505 ERR("Query failed\n");
5506 goto done;
5508 key = MSI_RecordGetString(row, 6);
5509 file = msi_get_loaded_file(package, key);
5510 msiobj_release(&row->hdr);
5511 if (!file)
5513 ERR("Failed to load the service file\n");
5514 goto done;
5517 if (!args || !args[0]) image_path = file->TargetPath;
5518 else
5520 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5521 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5522 return ERROR_OUTOFMEMORY;
5524 strcpyW(image_path, file->TargetPath);
5525 strcatW(image_path, szSpace);
5526 strcatW(image_path, args);
5528 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5529 start_type, err_control, image_path, load_order,
5530 NULL, depends, serv_name, pass);
5532 if (!service)
5534 if (GetLastError() != ERROR_SERVICE_EXISTS)
5535 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5537 else if (sd.lpDescription)
5539 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5540 WARN("failed to set service description %u\n", GetLastError());
5543 if (image_path != file->TargetPath) msi_free(image_path);
5544 done:
5545 CloseServiceHandle(service);
5546 CloseServiceHandle(hscm);
5547 msi_free(name);
5548 msi_free(disp);
5549 msi_free(sd.lpDescription);
5550 msi_free(load_order);
5551 msi_free(serv_name);
5552 msi_free(pass);
5553 msi_free(depends);
5554 msi_free(args);
5556 return ERROR_SUCCESS;
5559 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5561 static const WCHAR query[] = {
5562 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5563 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5564 MSIQUERY *view;
5565 UINT rc;
5567 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5568 if (rc != ERROR_SUCCESS)
5569 return ERROR_SUCCESS;
5571 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5572 msiobj_release(&view->hdr);
5573 return rc;
5576 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5577 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5579 LPCWSTR *vector, *temp_vector;
5580 LPWSTR p, q;
5581 DWORD sep_len;
5583 static const WCHAR separator[] = {'[','~',']',0};
5585 *numargs = 0;
5586 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5588 if (!args)
5589 return NULL;
5591 vector = msi_alloc(sizeof(LPWSTR));
5592 if (!vector)
5593 return NULL;
5595 p = args;
5598 (*numargs)++;
5599 vector[*numargs - 1] = p;
5601 if ((q = strstrW(p, separator)))
5603 *q = '\0';
5605 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5606 if (!temp_vector)
5608 msi_free(vector);
5609 return NULL;
5611 vector = temp_vector;
5613 p = q + sep_len;
5615 } while (q);
5617 return vector;
5620 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5622 MSIPACKAGE *package = param;
5623 MSICOMPONENT *comp;
5624 MSIRECORD *uirow;
5625 SC_HANDLE scm = NULL, service = NULL;
5626 LPCWSTR component, *vector = NULL;
5627 LPWSTR name, args, display_name = NULL;
5628 DWORD event, numargs, len, wait, dummy;
5629 UINT r = ERROR_FUNCTION_FAILED;
5630 SERVICE_STATUS_PROCESS status;
5631 ULONGLONG start_time;
5633 component = MSI_RecordGetString(rec, 6);
5634 comp = msi_get_loaded_component(package, component);
5635 if (!comp)
5636 return ERROR_SUCCESS;
5638 comp->Action = msi_get_component_action( package, comp );
5639 if (comp->Action != INSTALLSTATE_LOCAL)
5641 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5642 return ERROR_SUCCESS;
5645 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5646 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5647 event = MSI_RecordGetInteger(rec, 3);
5648 wait = MSI_RecordGetInteger(rec, 5);
5650 if (!(event & msidbServiceControlEventStart))
5652 r = ERROR_SUCCESS;
5653 goto done;
5656 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5657 if (!scm)
5659 ERR("Failed to open the service control manager\n");
5660 goto done;
5663 len = 0;
5664 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5665 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5667 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5668 GetServiceDisplayNameW( scm, name, display_name, &len );
5671 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5672 if (!service)
5674 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5675 goto done;
5678 vector = msi_service_args_to_vector(args, &numargs);
5680 if (!StartServiceW(service, numargs, vector) &&
5681 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5683 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5684 goto done;
5687 r = ERROR_SUCCESS;
5688 if (wait)
5690 /* wait for at most 30 seconds for the service to be up and running */
5691 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5692 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5694 TRACE("failed to query service status (%u)\n", GetLastError());
5695 goto done;
5697 start_time = GetTickCount64();
5698 while (status.dwCurrentState == SERVICE_START_PENDING)
5700 if (GetTickCount64() - start_time > 30000) break;
5701 Sleep(1000);
5702 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5703 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5705 TRACE("failed to query service status (%u)\n", GetLastError());
5706 goto done;
5709 if (status.dwCurrentState != SERVICE_RUNNING)
5711 WARN("service failed to start %u\n", status.dwCurrentState);
5712 r = ERROR_FUNCTION_FAILED;
5716 done:
5717 uirow = MSI_CreateRecord( 2 );
5718 MSI_RecordSetStringW( uirow, 1, display_name );
5719 MSI_RecordSetStringW( uirow, 2, name );
5720 msi_ui_actiondata( package, szStartServices, uirow );
5721 msiobj_release( &uirow->hdr );
5723 CloseServiceHandle(service);
5724 CloseServiceHandle(scm);
5726 msi_free(name);
5727 msi_free(args);
5728 msi_free(vector);
5729 msi_free(display_name);
5730 return r;
5733 static UINT ACTION_StartServices( MSIPACKAGE *package )
5735 static const WCHAR query[] = {
5736 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5737 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5738 MSIQUERY *view;
5739 UINT rc;
5741 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5742 if (rc != ERROR_SUCCESS)
5743 return ERROR_SUCCESS;
5745 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5746 msiobj_release(&view->hdr);
5747 return rc;
5750 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5752 DWORD i, needed, count;
5753 ENUM_SERVICE_STATUSW *dependencies;
5754 SERVICE_STATUS ss;
5755 SC_HANDLE depserv;
5757 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5758 0, &needed, &count))
5759 return TRUE;
5761 if (GetLastError() != ERROR_MORE_DATA)
5762 return FALSE;
5764 dependencies = msi_alloc(needed);
5765 if (!dependencies)
5766 return FALSE;
5768 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5769 needed, &needed, &count))
5770 goto error;
5772 for (i = 0; i < count; i++)
5774 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5775 SERVICE_STOP | SERVICE_QUERY_STATUS);
5776 if (!depserv)
5777 goto error;
5779 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5780 goto error;
5783 return TRUE;
5785 error:
5786 msi_free(dependencies);
5787 return FALSE;
5790 static UINT stop_service( LPCWSTR name )
5792 SC_HANDLE scm = NULL, service = NULL;
5793 SERVICE_STATUS status;
5794 SERVICE_STATUS_PROCESS ssp;
5795 DWORD needed;
5797 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5798 if (!scm)
5800 WARN("Failed to open the SCM: %d\n", GetLastError());
5801 goto done;
5804 service = OpenServiceW(scm, name,
5805 SERVICE_STOP |
5806 SERVICE_QUERY_STATUS |
5807 SERVICE_ENUMERATE_DEPENDENTS);
5808 if (!service)
5810 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5811 goto done;
5814 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5815 sizeof(SERVICE_STATUS_PROCESS), &needed))
5817 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5818 goto done;
5821 if (ssp.dwCurrentState == SERVICE_STOPPED)
5822 goto done;
5824 stop_service_dependents(scm, service);
5826 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5827 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5829 done:
5830 CloseServiceHandle(service);
5831 CloseServiceHandle(scm);
5833 return ERROR_SUCCESS;
5836 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5838 MSIPACKAGE *package = param;
5839 MSICOMPONENT *comp;
5840 MSIRECORD *uirow;
5841 LPCWSTR component;
5842 LPWSTR name = NULL, display_name = NULL;
5843 DWORD event, len;
5844 SC_HANDLE scm;
5846 event = MSI_RecordGetInteger( rec, 3 );
5847 if (!(event & msidbServiceControlEventStop))
5848 return ERROR_SUCCESS;
5850 component = MSI_RecordGetString( rec, 6 );
5851 comp = msi_get_loaded_component( package, component );
5852 if (!comp)
5853 return ERROR_SUCCESS;
5855 comp->Action = msi_get_component_action( package, comp );
5856 if (comp->Action != INSTALLSTATE_ABSENT)
5858 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5859 return ERROR_SUCCESS;
5862 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5863 if (!scm)
5865 ERR("Failed to open the service control manager\n");
5866 goto done;
5869 len = 0;
5870 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5871 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5873 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5874 GetServiceDisplayNameW( scm, name, display_name, &len );
5876 CloseServiceHandle( scm );
5878 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5879 stop_service( name );
5881 done:
5882 uirow = MSI_CreateRecord( 2 );
5883 MSI_RecordSetStringW( uirow, 1, display_name );
5884 MSI_RecordSetStringW( uirow, 2, name );
5885 msi_ui_actiondata( package, szStopServices, uirow );
5886 msiobj_release( &uirow->hdr );
5888 msi_free( name );
5889 msi_free( display_name );
5890 return ERROR_SUCCESS;
5893 static UINT ACTION_StopServices( MSIPACKAGE *package )
5895 static const WCHAR query[] = {
5896 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5897 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5898 MSIQUERY *view;
5899 UINT rc;
5901 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5902 if (rc != ERROR_SUCCESS)
5903 return ERROR_SUCCESS;
5905 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5906 msiobj_release(&view->hdr);
5907 return rc;
5910 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5912 MSIPACKAGE *package = param;
5913 MSICOMPONENT *comp;
5914 MSIRECORD *uirow;
5915 LPWSTR name = NULL, display_name = NULL;
5916 DWORD event, len;
5917 SC_HANDLE scm = NULL, service = NULL;
5919 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
5920 if (!comp)
5921 return ERROR_SUCCESS;
5923 event = MSI_RecordGetInteger( rec, 3 );
5924 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5926 comp->Action = msi_get_component_action( package, comp );
5927 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
5928 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
5930 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
5931 msi_free( name );
5932 return ERROR_SUCCESS;
5934 stop_service( name );
5936 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5937 if (!scm)
5939 WARN("Failed to open the SCM: %d\n", GetLastError());
5940 goto done;
5943 len = 0;
5944 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5945 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5947 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5948 GetServiceDisplayNameW( scm, name, display_name, &len );
5951 service = OpenServiceW( scm, name, DELETE );
5952 if (!service)
5954 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5955 goto done;
5958 if (!DeleteService( service ))
5959 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5961 done:
5962 uirow = MSI_CreateRecord( 2 );
5963 MSI_RecordSetStringW( uirow, 1, display_name );
5964 MSI_RecordSetStringW( uirow, 2, name );
5965 msi_ui_actiondata( package, szDeleteServices, uirow );
5966 msiobj_release( &uirow->hdr );
5968 CloseServiceHandle( service );
5969 CloseServiceHandle( scm );
5970 msi_free( name );
5971 msi_free( display_name );
5973 return ERROR_SUCCESS;
5976 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5978 static const WCHAR query[] = {
5979 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5980 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5981 MSIQUERY *view;
5982 UINT rc;
5984 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5985 if (rc != ERROR_SUCCESS)
5986 return ERROR_SUCCESS;
5988 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5989 msiobj_release( &view->hdr );
5990 return rc;
5993 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5995 MSIPACKAGE *package = param;
5996 LPWSTR driver, driver_path, ptr;
5997 WCHAR outpath[MAX_PATH];
5998 MSIFILE *driver_file = NULL, *setup_file = NULL;
5999 MSICOMPONENT *comp;
6000 MSIRECORD *uirow;
6001 LPCWSTR desc, file_key, component;
6002 DWORD len, usage;
6003 UINT r = ERROR_SUCCESS;
6005 static const WCHAR driver_fmt[] = {
6006 'D','r','i','v','e','r','=','%','s',0};
6007 static const WCHAR setup_fmt[] = {
6008 'S','e','t','u','p','=','%','s',0};
6009 static const WCHAR usage_fmt[] = {
6010 'F','i','l','e','U','s','a','g','e','=','1',0};
6012 component = MSI_RecordGetString( rec, 2 );
6013 comp = msi_get_loaded_component( package, component );
6014 if (!comp)
6015 return ERROR_SUCCESS;
6017 comp->Action = msi_get_component_action( package, comp );
6018 if (comp->Action != INSTALLSTATE_LOCAL)
6020 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6021 return ERROR_SUCCESS;
6023 desc = MSI_RecordGetString(rec, 3);
6025 file_key = MSI_RecordGetString( rec, 4 );
6026 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6028 file_key = MSI_RecordGetString( rec, 5 );
6029 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6031 if (!driver_file)
6033 ERR("ODBC Driver entry not found!\n");
6034 return ERROR_FUNCTION_FAILED;
6037 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6038 if (setup_file)
6039 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6040 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6042 driver = msi_alloc(len * sizeof(WCHAR));
6043 if (!driver)
6044 return ERROR_OUTOFMEMORY;
6046 ptr = driver;
6047 lstrcpyW(ptr, desc);
6048 ptr += lstrlenW(ptr) + 1;
6050 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6051 ptr += len + 1;
6053 if (setup_file)
6055 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6056 ptr += len + 1;
6059 lstrcpyW(ptr, usage_fmt);
6060 ptr += lstrlenW(ptr) + 1;
6061 *ptr = '\0';
6063 driver_path = strdupW(driver_file->TargetPath);
6064 ptr = strrchrW(driver_path, '\\');
6065 if (ptr) *ptr = '\0';
6067 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6068 NULL, ODBC_INSTALL_COMPLETE, &usage))
6070 ERR("Failed to install SQL driver!\n");
6071 r = ERROR_FUNCTION_FAILED;
6074 uirow = MSI_CreateRecord( 5 );
6075 MSI_RecordSetStringW( uirow, 1, desc );
6076 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6077 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6078 msi_ui_actiondata( package, szInstallODBC, uirow );
6079 msiobj_release( &uirow->hdr );
6081 msi_free(driver);
6082 msi_free(driver_path);
6084 return r;
6087 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6089 MSIPACKAGE *package = param;
6090 LPWSTR translator, translator_path, ptr;
6091 WCHAR outpath[MAX_PATH];
6092 MSIFILE *translator_file = NULL, *setup_file = NULL;
6093 MSICOMPONENT *comp;
6094 MSIRECORD *uirow;
6095 LPCWSTR desc, file_key, component;
6096 DWORD len, usage;
6097 UINT r = ERROR_SUCCESS;
6099 static const WCHAR translator_fmt[] = {
6100 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6101 static const WCHAR setup_fmt[] = {
6102 'S','e','t','u','p','=','%','s',0};
6104 component = MSI_RecordGetString( rec, 2 );
6105 comp = msi_get_loaded_component( package, component );
6106 if (!comp)
6107 return ERROR_SUCCESS;
6109 comp->Action = msi_get_component_action( package, comp );
6110 if (comp->Action != INSTALLSTATE_LOCAL)
6112 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6113 return ERROR_SUCCESS;
6115 desc = MSI_RecordGetString(rec, 3);
6117 file_key = MSI_RecordGetString( rec, 4 );
6118 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6120 file_key = MSI_RecordGetString( rec, 5 );
6121 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6123 if (!translator_file)
6125 ERR("ODBC Translator entry not found!\n");
6126 return ERROR_FUNCTION_FAILED;
6129 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6130 if (setup_file)
6131 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6133 translator = msi_alloc(len * sizeof(WCHAR));
6134 if (!translator)
6135 return ERROR_OUTOFMEMORY;
6137 ptr = translator;
6138 lstrcpyW(ptr, desc);
6139 ptr += lstrlenW(ptr) + 1;
6141 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6142 ptr += len + 1;
6144 if (setup_file)
6146 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6147 ptr += len + 1;
6149 *ptr = '\0';
6151 translator_path = strdupW(translator_file->TargetPath);
6152 ptr = strrchrW(translator_path, '\\');
6153 if (ptr) *ptr = '\0';
6155 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6156 NULL, ODBC_INSTALL_COMPLETE, &usage))
6158 ERR("Failed to install SQL translator!\n");
6159 r = ERROR_FUNCTION_FAILED;
6162 uirow = MSI_CreateRecord( 5 );
6163 MSI_RecordSetStringW( uirow, 1, desc );
6164 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6165 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6166 msi_ui_actiondata( package, szInstallODBC, uirow );
6167 msiobj_release( &uirow->hdr );
6169 msi_free(translator);
6170 msi_free(translator_path);
6172 return r;
6175 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6177 MSIPACKAGE *package = param;
6178 MSICOMPONENT *comp;
6179 LPWSTR attrs;
6180 LPCWSTR desc, driver, component;
6181 WORD request = ODBC_ADD_SYS_DSN;
6182 INT registration;
6183 DWORD len;
6184 UINT r = ERROR_SUCCESS;
6185 MSIRECORD *uirow;
6187 static const WCHAR attrs_fmt[] = {
6188 'D','S','N','=','%','s',0 };
6190 component = MSI_RecordGetString( rec, 2 );
6191 comp = msi_get_loaded_component( package, component );
6192 if (!comp)
6193 return ERROR_SUCCESS;
6195 comp->Action = msi_get_component_action( package, comp );
6196 if (comp->Action != INSTALLSTATE_LOCAL)
6198 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6199 return ERROR_SUCCESS;
6202 desc = MSI_RecordGetString(rec, 3);
6203 driver = MSI_RecordGetString(rec, 4);
6204 registration = MSI_RecordGetInteger(rec, 5);
6206 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6207 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6209 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6210 attrs = msi_alloc(len * sizeof(WCHAR));
6211 if (!attrs)
6212 return ERROR_OUTOFMEMORY;
6214 len = sprintfW(attrs, attrs_fmt, desc);
6215 attrs[len + 1] = 0;
6217 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6219 ERR("Failed to install SQL data source!\n");
6220 r = ERROR_FUNCTION_FAILED;
6223 uirow = MSI_CreateRecord( 5 );
6224 MSI_RecordSetStringW( uirow, 1, desc );
6225 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6226 MSI_RecordSetInteger( uirow, 3, request );
6227 msi_ui_actiondata( package, szInstallODBC, uirow );
6228 msiobj_release( &uirow->hdr );
6230 msi_free(attrs);
6232 return r;
6235 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6237 static const WCHAR driver_query[] = {
6238 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6239 'O','D','B','C','D','r','i','v','e','r',0};
6240 static const WCHAR translator_query[] = {
6241 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6242 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6243 static const WCHAR source_query[] = {
6244 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6245 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6246 MSIQUERY *view;
6247 UINT rc;
6249 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6250 if (rc == ERROR_SUCCESS)
6252 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6253 msiobj_release(&view->hdr);
6254 if (rc != ERROR_SUCCESS)
6255 return rc;
6257 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6258 if (rc == ERROR_SUCCESS)
6260 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6261 msiobj_release(&view->hdr);
6262 if (rc != ERROR_SUCCESS)
6263 return rc;
6265 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6266 if (rc == ERROR_SUCCESS)
6268 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6269 msiobj_release(&view->hdr);
6270 if (rc != ERROR_SUCCESS)
6271 return rc;
6273 return ERROR_SUCCESS;
6276 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6278 MSIPACKAGE *package = param;
6279 MSICOMPONENT *comp;
6280 MSIRECORD *uirow;
6281 DWORD usage;
6282 LPCWSTR desc, component;
6284 component = MSI_RecordGetString( rec, 2 );
6285 comp = msi_get_loaded_component( package, component );
6286 if (!comp)
6287 return ERROR_SUCCESS;
6289 comp->Action = msi_get_component_action( package, comp );
6290 if (comp->Action != INSTALLSTATE_ABSENT)
6292 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6293 return ERROR_SUCCESS;
6296 desc = MSI_RecordGetString( rec, 3 );
6297 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6299 WARN("Failed to remove ODBC driver\n");
6301 else if (!usage)
6303 FIXME("Usage count reached 0\n");
6306 uirow = MSI_CreateRecord( 2 );
6307 MSI_RecordSetStringW( uirow, 1, desc );
6308 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6309 msi_ui_actiondata( package, szRemoveODBC, uirow );
6310 msiobj_release( &uirow->hdr );
6312 return ERROR_SUCCESS;
6315 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6317 MSIPACKAGE *package = param;
6318 MSICOMPONENT *comp;
6319 MSIRECORD *uirow;
6320 DWORD usage;
6321 LPCWSTR desc, component;
6323 component = MSI_RecordGetString( rec, 2 );
6324 comp = msi_get_loaded_component( package, component );
6325 if (!comp)
6326 return ERROR_SUCCESS;
6328 comp->Action = msi_get_component_action( package, comp );
6329 if (comp->Action != INSTALLSTATE_ABSENT)
6331 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6332 return ERROR_SUCCESS;
6335 desc = MSI_RecordGetString( rec, 3 );
6336 if (!SQLRemoveTranslatorW( desc, &usage ))
6338 WARN("Failed to remove ODBC translator\n");
6340 else if (!usage)
6342 FIXME("Usage count reached 0\n");
6345 uirow = MSI_CreateRecord( 2 );
6346 MSI_RecordSetStringW( uirow, 1, desc );
6347 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6348 msi_ui_actiondata( package, szRemoveODBC, uirow );
6349 msiobj_release( &uirow->hdr );
6351 return ERROR_SUCCESS;
6354 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6356 MSIPACKAGE *package = param;
6357 MSICOMPONENT *comp;
6358 MSIRECORD *uirow;
6359 LPWSTR attrs;
6360 LPCWSTR desc, driver, component;
6361 WORD request = ODBC_REMOVE_SYS_DSN;
6362 INT registration;
6363 DWORD len;
6365 static const WCHAR attrs_fmt[] = {
6366 'D','S','N','=','%','s',0 };
6368 component = MSI_RecordGetString( rec, 2 );
6369 comp = msi_get_loaded_component( package, component );
6370 if (!comp)
6371 return ERROR_SUCCESS;
6373 comp->Action = msi_get_component_action( package, comp );
6374 if (comp->Action != INSTALLSTATE_ABSENT)
6376 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6377 return ERROR_SUCCESS;
6380 desc = MSI_RecordGetString( rec, 3 );
6381 driver = MSI_RecordGetString( rec, 4 );
6382 registration = MSI_RecordGetInteger( rec, 5 );
6384 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6385 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6387 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6388 attrs = msi_alloc( len * sizeof(WCHAR) );
6389 if (!attrs)
6390 return ERROR_OUTOFMEMORY;
6392 FIXME("Use ODBCSourceAttribute table\n");
6394 len = sprintfW( attrs, attrs_fmt, desc );
6395 attrs[len + 1] = 0;
6397 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6399 WARN("Failed to remove ODBC data source\n");
6401 msi_free( attrs );
6403 uirow = MSI_CreateRecord( 3 );
6404 MSI_RecordSetStringW( uirow, 1, desc );
6405 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6406 MSI_RecordSetInteger( uirow, 3, request );
6407 msi_ui_actiondata( package, szRemoveODBC, uirow );
6408 msiobj_release( &uirow->hdr );
6410 return ERROR_SUCCESS;
6413 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6415 static const WCHAR driver_query[] = {
6416 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6417 'O','D','B','C','D','r','i','v','e','r',0};
6418 static const WCHAR translator_query[] = {
6419 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6420 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6421 static const WCHAR source_query[] = {
6422 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6423 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6424 MSIQUERY *view;
6425 UINT rc;
6427 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6428 if (rc == ERROR_SUCCESS)
6430 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6431 msiobj_release( &view->hdr );
6432 if (rc != ERROR_SUCCESS)
6433 return rc;
6435 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6436 if (rc == ERROR_SUCCESS)
6438 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6439 msiobj_release( &view->hdr );
6440 if (rc != ERROR_SUCCESS)
6441 return rc;
6443 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6444 if (rc == ERROR_SUCCESS)
6446 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6447 msiobj_release( &view->hdr );
6448 if (rc != ERROR_SUCCESS)
6449 return rc;
6451 return ERROR_SUCCESS;
6454 #define ENV_ACT_SETALWAYS 0x1
6455 #define ENV_ACT_SETABSENT 0x2
6456 #define ENV_ACT_REMOVE 0x4
6457 #define ENV_ACT_REMOVEMATCH 0x8
6459 #define ENV_MOD_MACHINE 0x20000000
6460 #define ENV_MOD_APPEND 0x40000000
6461 #define ENV_MOD_PREFIX 0x80000000
6462 #define ENV_MOD_MASK 0xC0000000
6464 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6466 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6468 LPCWSTR cptr = *name;
6470 static const WCHAR prefix[] = {'[','~',']',0};
6471 static const int prefix_len = 3;
6473 *flags = 0;
6474 while (*cptr)
6476 if (*cptr == '=')
6477 *flags |= ENV_ACT_SETALWAYS;
6478 else if (*cptr == '+')
6479 *flags |= ENV_ACT_SETABSENT;
6480 else if (*cptr == '-')
6481 *flags |= ENV_ACT_REMOVE;
6482 else if (*cptr == '!')
6483 *flags |= ENV_ACT_REMOVEMATCH;
6484 else if (*cptr == '*')
6485 *flags |= ENV_MOD_MACHINE;
6486 else
6487 break;
6489 cptr++;
6490 (*name)++;
6493 if (!*cptr)
6495 ERR("Missing environment variable\n");
6496 return ERROR_FUNCTION_FAILED;
6499 if (*value)
6501 LPCWSTR ptr = *value;
6502 if (!strncmpW(ptr, prefix, prefix_len))
6504 if (ptr[prefix_len] == szSemiColon[0])
6506 *flags |= ENV_MOD_APPEND;
6507 *value += lstrlenW(prefix);
6509 else
6511 *value = NULL;
6514 else if (lstrlenW(*value) >= prefix_len)
6516 ptr += lstrlenW(ptr) - prefix_len;
6517 if (!strcmpW( ptr, prefix ))
6519 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6521 *flags |= ENV_MOD_PREFIX;
6522 /* the "[~]" will be removed by deformat_string */;
6524 else
6526 *value = NULL;
6532 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6533 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6534 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6535 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6537 ERR("Invalid flags: %08x\n", *flags);
6538 return ERROR_FUNCTION_FAILED;
6541 if (!*flags)
6542 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6544 return ERROR_SUCCESS;
6547 static UINT open_env_key( DWORD flags, HKEY *key )
6549 static const WCHAR user_env[] =
6550 {'E','n','v','i','r','o','n','m','e','n','t',0};
6551 static const WCHAR machine_env[] =
6552 {'S','y','s','t','e','m','\\',
6553 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6554 'C','o','n','t','r','o','l','\\',
6555 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6556 'E','n','v','i','r','o','n','m','e','n','t',0};
6557 const WCHAR *env;
6558 HKEY root;
6559 LONG res;
6561 if (flags & ENV_MOD_MACHINE)
6563 env = machine_env;
6564 root = HKEY_LOCAL_MACHINE;
6566 else
6568 env = user_env;
6569 root = HKEY_CURRENT_USER;
6572 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6573 if (res != ERROR_SUCCESS)
6575 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6576 return ERROR_FUNCTION_FAILED;
6579 return ERROR_SUCCESS;
6582 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6584 MSIPACKAGE *package = param;
6585 LPCWSTR name, value, component;
6586 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6587 DWORD flags, type, size;
6588 UINT res;
6589 HKEY env = NULL;
6590 MSICOMPONENT *comp;
6591 MSIRECORD *uirow;
6592 int action = 0;
6594 component = MSI_RecordGetString(rec, 4);
6595 comp = msi_get_loaded_component(package, component);
6596 if (!comp)
6597 return ERROR_SUCCESS;
6599 comp->Action = msi_get_component_action( package, comp );
6600 if (comp->Action != INSTALLSTATE_LOCAL)
6602 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6603 return ERROR_SUCCESS;
6605 name = MSI_RecordGetString(rec, 2);
6606 value = MSI_RecordGetString(rec, 3);
6608 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6610 res = env_parse_flags(&name, &value, &flags);
6611 if (res != ERROR_SUCCESS || !value)
6612 goto done;
6614 if (value && !deformat_string(package, value, &deformatted))
6616 res = ERROR_OUTOFMEMORY;
6617 goto done;
6620 value = deformatted;
6622 res = open_env_key( flags, &env );
6623 if (res != ERROR_SUCCESS)
6624 goto done;
6626 if (flags & ENV_MOD_MACHINE)
6627 action |= 0x20000000;
6629 size = 0;
6630 type = REG_SZ;
6631 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6632 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6633 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6634 goto done;
6636 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6638 action = 0x2;
6640 /* Nothing to do. */
6641 if (!value)
6643 res = ERROR_SUCCESS;
6644 goto done;
6647 /* If we are appending but the string was empty, strip ; */
6648 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6650 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6651 newval = strdupW(value);
6652 if (!newval)
6654 res = ERROR_OUTOFMEMORY;
6655 goto done;
6658 else
6660 action = 0x1;
6662 /* Contrary to MSDN, +-variable to [~];path works */
6663 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6665 res = ERROR_SUCCESS;
6666 goto done;
6669 data = msi_alloc(size);
6670 if (!data)
6672 RegCloseKey(env);
6673 return ERROR_OUTOFMEMORY;
6676 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6677 if (res != ERROR_SUCCESS)
6678 goto done;
6680 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6682 action = 0x4;
6683 res = RegDeleteValueW(env, name);
6684 if (res != ERROR_SUCCESS)
6685 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6686 goto done;
6689 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6690 if (flags & ENV_MOD_MASK)
6692 DWORD mod_size;
6693 int multiplier = 0;
6694 if (flags & ENV_MOD_APPEND) multiplier++;
6695 if (flags & ENV_MOD_PREFIX) multiplier++;
6696 mod_size = lstrlenW(value) * multiplier;
6697 size += mod_size * sizeof(WCHAR);
6700 newval = msi_alloc(size);
6701 ptr = newval;
6702 if (!newval)
6704 res = ERROR_OUTOFMEMORY;
6705 goto done;
6708 if (flags & ENV_MOD_PREFIX)
6710 lstrcpyW(newval, value);
6711 ptr = newval + lstrlenW(value);
6712 action |= 0x80000000;
6715 lstrcpyW(ptr, data);
6717 if (flags & ENV_MOD_APPEND)
6719 lstrcatW(newval, value);
6720 action |= 0x40000000;
6723 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6724 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6725 if (res)
6727 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6730 done:
6731 uirow = MSI_CreateRecord( 3 );
6732 MSI_RecordSetStringW( uirow, 1, name );
6733 MSI_RecordSetStringW( uirow, 2, newval );
6734 MSI_RecordSetInteger( uirow, 3, action );
6735 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6736 msiobj_release( &uirow->hdr );
6738 if (env) RegCloseKey(env);
6739 msi_free(deformatted);
6740 msi_free(data);
6741 msi_free(newval);
6742 return res;
6745 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6747 static const WCHAR query[] = {
6748 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6749 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6750 MSIQUERY *view;
6751 UINT rc;
6753 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6754 if (rc != ERROR_SUCCESS)
6755 return ERROR_SUCCESS;
6757 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6758 msiobj_release(&view->hdr);
6759 return rc;
6762 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6764 MSIPACKAGE *package = param;
6765 LPCWSTR name, value, component;
6766 LPWSTR deformatted = NULL;
6767 DWORD flags;
6768 HKEY env;
6769 MSICOMPONENT *comp;
6770 MSIRECORD *uirow;
6771 int action = 0;
6772 LONG res;
6773 UINT r;
6775 component = MSI_RecordGetString( rec, 4 );
6776 comp = msi_get_loaded_component( package, component );
6777 if (!comp)
6778 return ERROR_SUCCESS;
6780 comp->Action = msi_get_component_action( package, comp );
6781 if (comp->Action != INSTALLSTATE_ABSENT)
6783 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6784 return ERROR_SUCCESS;
6786 name = MSI_RecordGetString( rec, 2 );
6787 value = MSI_RecordGetString( rec, 3 );
6789 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6791 r = env_parse_flags( &name, &value, &flags );
6792 if (r != ERROR_SUCCESS)
6793 return r;
6795 if (!(flags & ENV_ACT_REMOVE))
6797 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6798 return ERROR_SUCCESS;
6801 if (value && !deformat_string( package, value, &deformatted ))
6802 return ERROR_OUTOFMEMORY;
6804 value = deformatted;
6806 r = open_env_key( flags, &env );
6807 if (r != ERROR_SUCCESS)
6809 r = ERROR_SUCCESS;
6810 goto done;
6813 if (flags & ENV_MOD_MACHINE)
6814 action |= 0x20000000;
6816 TRACE("Removing %s\n", debugstr_w(name));
6818 res = RegDeleteValueW( env, name );
6819 if (res != ERROR_SUCCESS)
6821 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6822 r = ERROR_SUCCESS;
6825 done:
6826 uirow = MSI_CreateRecord( 3 );
6827 MSI_RecordSetStringW( uirow, 1, name );
6828 MSI_RecordSetStringW( uirow, 2, value );
6829 MSI_RecordSetInteger( uirow, 3, action );
6830 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6831 msiobj_release( &uirow->hdr );
6833 if (env) RegCloseKey( env );
6834 msi_free( deformatted );
6835 return r;
6838 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6840 static const WCHAR query[] = {
6841 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6842 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6843 MSIQUERY *view;
6844 UINT rc;
6846 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6847 if (rc != ERROR_SUCCESS)
6848 return ERROR_SUCCESS;
6850 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6851 msiobj_release( &view->hdr );
6852 return rc;
6855 UINT msi_validate_product_id( MSIPACKAGE *package )
6857 LPWSTR key, template, id;
6858 UINT r = ERROR_SUCCESS;
6860 id = msi_dup_property( package->db, szProductID );
6861 if (id)
6863 msi_free( id );
6864 return ERROR_SUCCESS;
6866 template = msi_dup_property( package->db, szPIDTemplate );
6867 key = msi_dup_property( package->db, szPIDKEY );
6868 if (key && template)
6870 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6871 r = msi_set_property( package->db, szProductID, key );
6873 msi_free( template );
6874 msi_free( key );
6875 return r;
6878 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6880 return msi_validate_product_id( package );
6883 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6885 TRACE("\n");
6886 package->need_reboot_at_end = 1;
6887 return ERROR_SUCCESS;
6890 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6892 static const WCHAR szAvailableFreeReg[] =
6893 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6894 MSIRECORD *uirow;
6895 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6897 TRACE("%p %d kilobytes\n", package, space);
6899 uirow = MSI_CreateRecord( 1 );
6900 MSI_RecordSetInteger( uirow, 1, space );
6901 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6902 msiobj_release( &uirow->hdr );
6904 return ERROR_SUCCESS;
6907 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6909 TRACE("%p\n", package);
6911 msi_set_property( package->db, szRollbackDisabled, szOne );
6912 return ERROR_SUCCESS;
6915 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6917 FIXME("%p\n", package);
6918 return ERROR_SUCCESS;
6921 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6923 static const WCHAR driver_query[] = {
6924 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6925 'O','D','B','C','D','r','i','v','e','r',0};
6926 static const WCHAR translator_query[] = {
6927 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6928 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6929 MSIQUERY *view;
6930 UINT r, count;
6932 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6933 if (r == ERROR_SUCCESS)
6935 count = 0;
6936 r = MSI_IterateRecords( view, &count, NULL, package );
6937 msiobj_release( &view->hdr );
6938 if (r != ERROR_SUCCESS)
6939 return r;
6940 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6942 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6943 if (r == ERROR_SUCCESS)
6945 count = 0;
6946 r = MSI_IterateRecords( view, &count, NULL, package );
6947 msiobj_release( &view->hdr );
6948 if (r != ERROR_SUCCESS)
6949 return r;
6950 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6952 return ERROR_SUCCESS;
6955 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6957 MSIPACKAGE *package = param;
6958 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6959 WCHAR *value;
6961 if ((value = msi_dup_property( package->db, property )))
6963 FIXME("remove %s\n", debugstr_w(value));
6964 msi_free( value );
6966 return ERROR_SUCCESS;
6969 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6971 static const WCHAR query[] = {
6972 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6973 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6974 MSIQUERY *view;
6975 UINT r;
6977 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6978 if (r == ERROR_SUCCESS)
6980 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6981 msiobj_release( &view->hdr );
6982 if (r != ERROR_SUCCESS)
6983 return r;
6985 return ERROR_SUCCESS;
6988 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6990 MSIPACKAGE *package = param;
6991 int attributes = MSI_RecordGetInteger( rec, 5 );
6993 if (attributes & msidbUpgradeAttributesMigrateFeatures)
6995 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
6996 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
6997 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
6998 const WCHAR *language = MSI_RecordGetString( rec, 4 );
6999 HKEY hkey;
7000 UINT r;
7002 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7004 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7005 if (r != ERROR_SUCCESS)
7006 return ERROR_SUCCESS;
7008 else
7010 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7011 if (r != ERROR_SUCCESS)
7012 return ERROR_SUCCESS;
7014 RegCloseKey( hkey );
7016 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7017 debugstr_w(upgrade_code), debugstr_w(version_min),
7018 debugstr_w(version_max), debugstr_w(language));
7020 return ERROR_SUCCESS;
7023 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7025 static const WCHAR query[] = {
7026 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7027 'U','p','g','r','a','d','e',0};
7028 MSIQUERY *view;
7029 UINT r;
7031 if (msi_get_property_int( package->db, szInstalled, 0 ))
7033 TRACE("product is installed, skipping action\n");
7034 return ERROR_SUCCESS;
7036 if (msi_get_property_int( package->db, szPreselected, 0 ))
7038 TRACE("Preselected property is set, not migrating feature states\n");
7039 return ERROR_SUCCESS;
7041 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7042 if (r == ERROR_SUCCESS)
7044 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7045 msiobj_release( &view->hdr );
7046 if (r != ERROR_SUCCESS)
7047 return r;
7049 return ERROR_SUCCESS;
7052 static void bind_image( const char *filename, const char *path )
7054 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7056 WARN("failed to bind image %u\n", GetLastError());
7060 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7062 UINT i;
7063 MSIFILE *file;
7064 MSIPACKAGE *package = param;
7065 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7066 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7067 char *filenameA, *pathA;
7068 WCHAR *pathW, **path_list;
7070 if (!(file = msi_get_loaded_file( package, key )))
7072 WARN("file %s not found\n", debugstr_w(key));
7073 return ERROR_SUCCESS;
7075 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7076 path_list = msi_split_string( paths, ';' );
7077 if (!path_list) bind_image( filenameA, NULL );
7078 else
7080 for (i = 0; path_list[i] && path_list[i][0]; i++)
7082 deformat_string( package, path_list[i], &pathW );
7083 if ((pathA = strdupWtoA( pathW )))
7085 bind_image( filenameA, pathA );
7086 msi_free( pathA );
7088 msi_free( pathW );
7091 msi_free( path_list );
7092 msi_free( filenameA );
7093 return ERROR_SUCCESS;
7096 static UINT ACTION_BindImage( MSIPACKAGE *package )
7098 static const WCHAR query[] = {
7099 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7100 'B','i','n','d','I','m','a','g','e',0};
7101 MSIQUERY *view;
7102 UINT r;
7104 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7105 if (r == ERROR_SUCCESS)
7107 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7108 msiobj_release( &view->hdr );
7109 if (r != ERROR_SUCCESS)
7110 return r;
7112 return ERROR_SUCCESS;
7115 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7117 static const WCHAR query[] = {
7118 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7119 MSIQUERY *view;
7120 DWORD count = 0;
7121 UINT r;
7123 r = MSI_OpenQuery( package->db, &view, query, table );
7124 if (r == ERROR_SUCCESS)
7126 r = MSI_IterateRecords(view, &count, NULL, package);
7127 msiobj_release(&view->hdr);
7128 if (r != ERROR_SUCCESS)
7129 return r;
7131 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7132 return ERROR_SUCCESS;
7135 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7137 static const WCHAR table[] = {
7138 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7139 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7142 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7144 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7145 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7148 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7150 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7151 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7154 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7156 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7157 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7160 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7162 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7163 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7166 static const struct
7168 const WCHAR *action;
7169 UINT (*handler)(MSIPACKAGE *);
7170 const WCHAR *action_rollback;
7172 StandardActions[] =
7174 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7175 { szAppSearch, ACTION_AppSearch, NULL },
7176 { szBindImage, ACTION_BindImage, NULL },
7177 { szCCPSearch, ACTION_CCPSearch, NULL },
7178 { szCostFinalize, ACTION_CostFinalize, NULL },
7179 { szCostInitialize, ACTION_CostInitialize, NULL },
7180 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7181 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7182 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7183 { szDisableRollback, ACTION_DisableRollback, NULL },
7184 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7185 { szExecuteAction, ACTION_ExecuteAction, NULL },
7186 { szFileCost, ACTION_FileCost, NULL },
7187 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7188 { szForceReboot, ACTION_ForceReboot, NULL },
7189 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7190 { szInstallExecute, ACTION_InstallExecute, NULL },
7191 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7192 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7193 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7194 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7195 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7196 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7197 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7198 { szInstallValidate, ACTION_InstallValidate, NULL },
7199 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7200 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7201 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7202 { szMoveFiles, ACTION_MoveFiles, NULL },
7203 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7204 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7205 { szPatchFiles, ACTION_PatchFiles, NULL },
7206 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7207 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7208 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7209 { szPublishProduct, ACTION_PublishProduct, NULL },
7210 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7211 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7212 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7213 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7214 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7215 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7216 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7217 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7218 { szRegisterUser, ACTION_RegisterUser, NULL },
7219 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7220 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7221 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7222 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7223 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7224 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7225 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7226 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7227 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7228 { szResolveSource, ACTION_ResolveSource, NULL },
7229 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7230 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7231 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7232 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7233 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7234 { szStartServices, ACTION_StartServices, szStopServices },
7235 { szStopServices, ACTION_StopServices, szStartServices },
7236 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7237 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7238 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7239 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7240 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7241 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7242 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7243 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7244 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7245 { szValidateProductID, ACTION_ValidateProductID, NULL },
7246 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7247 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7248 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7249 { NULL, NULL, NULL }
7252 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7254 BOOL ret = FALSE;
7255 UINT i;
7257 i = 0;
7258 while (StandardActions[i].action != NULL)
7260 if (!strcmpW( StandardActions[i].action, action ))
7262 ui_actionstart( package, action );
7263 if (StandardActions[i].handler)
7265 ui_actioninfo( package, action, TRUE, 0 );
7266 *rc = StandardActions[i].handler( package );
7267 ui_actioninfo( package, action, FALSE, *rc );
7269 if (StandardActions[i].action_rollback && !package->need_rollback)
7271 TRACE("scheduling rollback action\n");
7272 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7275 else
7277 FIXME("unhandled standard action %s\n", debugstr_w(action));
7278 *rc = ERROR_SUCCESS;
7280 ret = TRUE;
7281 break;
7283 i++;
7285 return ret;
7288 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7290 UINT rc = ERROR_SUCCESS;
7291 BOOL handled;
7293 TRACE("Performing action (%s)\n", debugstr_w(action));
7295 handled = ACTION_HandleStandardAction(package, action, &rc);
7297 if (!handled)
7298 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7300 if (!handled)
7302 WARN("unhandled msi action %s\n", debugstr_w(action));
7303 rc = ERROR_FUNCTION_NOT_CALLED;
7306 return rc;
7309 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7311 UINT rc = ERROR_SUCCESS;
7312 BOOL handled = FALSE;
7314 TRACE("Performing action (%s)\n", debugstr_w(action));
7316 handled = ACTION_HandleStandardAction(package, action, &rc);
7318 if (!handled)
7319 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7321 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7322 handled = TRUE;
7324 if (!handled)
7326 WARN("unhandled msi action %s\n", debugstr_w(action));
7327 rc = ERROR_FUNCTION_NOT_CALLED;
7330 return rc;
7333 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7335 UINT rc = ERROR_SUCCESS;
7336 MSIRECORD *row;
7338 static const WCHAR query[] =
7339 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7340 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7341 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7342 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7343 static const WCHAR ui_query[] =
7344 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7345 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7346 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7347 ' ', '=',' ','%','i',0};
7349 if (needs_ui_sequence(package))
7350 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7351 else
7352 row = MSI_QueryGetRecord(package->db, query, seq);
7354 if (row)
7356 LPCWSTR action, cond;
7358 TRACE("Running the actions\n");
7360 /* check conditions */
7361 cond = MSI_RecordGetString(row, 2);
7363 /* this is a hack to skip errors in the condition code */
7364 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7366 msiobj_release(&row->hdr);
7367 return ERROR_SUCCESS;
7370 action = MSI_RecordGetString(row, 1);
7371 if (!action)
7373 ERR("failed to fetch action\n");
7374 msiobj_release(&row->hdr);
7375 return ERROR_FUNCTION_FAILED;
7378 if (needs_ui_sequence(package))
7379 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7380 else
7381 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7383 msiobj_release(&row->hdr);
7386 return rc;
7389 /****************************************************
7390 * TOP level entry points
7391 *****************************************************/
7393 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7394 LPCWSTR szCommandLine )
7396 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7397 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7398 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7399 WCHAR *reinstall = NULL;
7400 BOOL ui_exists;
7401 UINT rc;
7403 msi_set_property( package->db, szAction, szInstall );
7405 package->script->InWhatSequence = SEQUENCE_INSTALL;
7407 if (szPackagePath)
7409 LPWSTR p, dir;
7410 LPCWSTR file;
7412 dir = strdupW(szPackagePath);
7413 p = strrchrW(dir, '\\');
7414 if (p)
7416 *(++p) = 0;
7417 file = szPackagePath + (p - dir);
7419 else
7421 msi_free(dir);
7422 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7423 GetCurrentDirectoryW(MAX_PATH, dir);
7424 lstrcatW(dir, szBackSlash);
7425 file = szPackagePath;
7428 msi_free( package->PackagePath );
7429 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7430 if (!package->PackagePath)
7432 msi_free(dir);
7433 return ERROR_OUTOFMEMORY;
7436 lstrcpyW(package->PackagePath, dir);
7437 lstrcatW(package->PackagePath, file);
7438 msi_free(dir);
7440 msi_set_sourcedir_props(package, FALSE);
7443 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7444 if (rc != ERROR_SUCCESS)
7445 return rc;
7447 msi_apply_transforms( package );
7448 msi_apply_patches( package );
7450 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7452 TRACE("setting reinstall property\n");
7453 msi_set_property( package->db, szReinstall, szAll );
7456 /* properties may have been added by a transform */
7457 msi_clone_properties( package );
7459 msi_parse_command_line( package, szCommandLine, FALSE );
7460 msi_adjust_privilege_properties( package );
7461 msi_set_context( package );
7463 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7465 TRACE("disabling rollback\n");
7466 msi_set_property( package->db, szRollbackDisabled, szOne );
7469 if (needs_ui_sequence( package))
7471 package->script->InWhatSequence |= SEQUENCE_UI;
7472 rc = ACTION_ProcessUISequence(package);
7473 ui_exists = ui_sequence_exists(package);
7474 if (rc == ERROR_SUCCESS || !ui_exists)
7476 package->script->InWhatSequence |= SEQUENCE_EXEC;
7477 rc = ACTION_ProcessExecSequence(package, ui_exists);
7480 else
7481 rc = ACTION_ProcessExecSequence(package, FALSE);
7483 package->script->CurrentlyScripting = FALSE;
7485 /* process the ending type action */
7486 if (rc == ERROR_SUCCESS)
7487 ACTION_PerformActionSequence(package, -1);
7488 else if (rc == ERROR_INSTALL_USEREXIT)
7489 ACTION_PerformActionSequence(package, -2);
7490 else if (rc == ERROR_INSTALL_SUSPEND)
7491 ACTION_PerformActionSequence(package, -4);
7492 else /* failed */
7494 ACTION_PerformActionSequence(package, -3);
7495 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7497 package->need_rollback = TRUE;
7501 /* finish up running custom actions */
7502 ACTION_FinishCustomActions(package);
7504 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7506 WARN("installation failed, running rollback script\n");
7507 execute_script( package, SCRIPT_ROLLBACK );
7509 msi_free( reinstall );
7511 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7512 return ERROR_SUCCESS_REBOOT_REQUIRED;
7514 return rc;