msi: Keep the UI level fixed during the lifetime of a package.
[wine/multimedia.git] / dlls / msi / action.c
blobb3472f88558dd561ebe81a710d93cebd3e9d9676
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 in_quotes = 1;
226 ignore = 1;
227 len++;
228 break;
229 case '"':
230 state = state_quote;
231 if (in_quotes && p[1] != '\"') count--;
232 else count++;
233 break;
234 default:
235 state = state_token;
236 in_quotes = 1;
237 len++;
238 break;
240 break;
242 case state_token:
243 switch (*p)
245 case '"':
246 state = state_quote;
247 if (in_quotes) count--;
248 else count++;
249 break;
250 case ' ':
251 state = state_whitespace;
252 if (!count) goto done;
253 in_quotes = 1;
254 len++;
255 break;
256 default:
257 if (!count) in_quotes = 0;
258 else in_quotes = 1;
259 len++;
260 break;
262 break;
264 case state_quote:
265 switch (*p)
267 case '"':
268 if (in_quotes && p[1] != '\"') count--;
269 else count++;
270 break;
271 case ' ':
272 state = state_whitespace;
273 if (!count || (count > 1 && !len)) goto done;
274 in_quotes = 1;
275 len++;
276 break;
277 default:
278 state = state_token;
279 if (!count) in_quotes = 0;
280 else in_quotes = 1;
281 len++;
282 break;
284 break;
286 default: break;
288 if (!ignore) *out++ = *p;
291 done:
292 if (!len) *value = 0;
293 else *out = 0;
295 *quotes = count;
296 return p - str;
299 static void remove_quotes( WCHAR *str )
301 WCHAR *p = str;
302 int len = strlenW( str );
304 while ((p = strchrW( p, '"' )))
306 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
307 p++;
311 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
312 BOOL preserve_case )
314 LPCWSTR ptr, ptr2;
315 int num_quotes;
316 DWORD len;
317 WCHAR *prop, *val;
318 UINT r;
320 if (!szCommandLine)
321 return ERROR_SUCCESS;
323 ptr = szCommandLine;
324 while (*ptr)
326 while (*ptr == ' ') ptr++;
327 if (!*ptr) break;
329 ptr2 = strchrW( ptr, '=' );
330 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
332 len = ptr2 - ptr;
333 if (!len) return ERROR_INVALID_COMMAND_LINE;
335 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
336 memcpy( prop, ptr, len * sizeof(WCHAR) );
337 prop[len] = 0;
338 if (!preserve_case) struprW( prop );
340 ptr2++;
341 while (*ptr2 == ' ') ptr2++;
343 num_quotes = 0;
344 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
345 len = parse_prop( ptr2, val, &num_quotes );
346 if (num_quotes % 2)
348 WARN("unbalanced quotes\n");
349 msi_free( val );
350 msi_free( prop );
351 return ERROR_INVALID_COMMAND_LINE;
353 remove_quotes( val );
354 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
356 r = msi_set_property( package->db, prop, val );
357 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
358 msi_reset_folders( package, TRUE );
360 msi_free( val );
361 msi_free( prop );
363 ptr = ptr2 + len;
366 return ERROR_SUCCESS;
369 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
371 LPCWSTR pc;
372 LPWSTR p, *ret = NULL;
373 UINT count = 0;
375 if (!str)
376 return ret;
378 /* count the number of substrings */
379 for ( pc = str, count = 0; pc; count++ )
381 pc = strchrW( pc, sep );
382 if (pc)
383 pc++;
386 /* allocate space for an array of substring pointers and the substrings */
387 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
388 (lstrlenW(str)+1) * sizeof(WCHAR) );
389 if (!ret)
390 return ret;
392 /* copy the string and set the pointers */
393 p = (LPWSTR) &ret[count+1];
394 lstrcpyW( p, str );
395 for( count = 0; (ret[count] = p); count++ )
397 p = strchrW( p, sep );
398 if (p)
399 *p++ = 0;
402 return ret;
405 static BOOL ui_sequence_exists( MSIPACKAGE *package )
407 static const WCHAR query [] = {
408 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
409 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
410 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
411 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
412 MSIQUERY *view;
413 UINT rc;
415 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
416 if (rc == ERROR_SUCCESS)
418 msiobj_release(&view->hdr);
419 return TRUE;
421 return FALSE;
424 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
426 LPWSTR source, check;
428 if (msi_get_property_int( package->db, szInstalled, 0 ))
430 HKEY hkey;
432 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
433 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
434 RegCloseKey( hkey );
436 else
438 LPWSTR p, db;
439 DWORD len;
441 db = msi_dup_property( package->db, szOriginalDatabase );
442 if (!db)
443 return ERROR_OUTOFMEMORY;
445 p = strrchrW( db, '\\' );
446 if (!p)
448 p = strrchrW( db, '/' );
449 if (!p)
451 msi_free(db);
452 return ERROR_SUCCESS;
456 len = p - db + 2;
457 source = msi_alloc( len * sizeof(WCHAR) );
458 lstrcpynW( source, db, len );
459 msi_free( db );
462 check = msi_dup_property( package->db, szSourceDir );
463 if (!check || replace)
465 UINT r = msi_set_property( package->db, szSourceDir, source );
466 if (r == ERROR_SUCCESS)
467 msi_reset_folders( package, TRUE );
469 msi_free( check );
471 check = msi_dup_property( package->db, szSOURCEDIR );
472 if (!check || replace)
473 msi_set_property( package->db, szSOURCEDIR, source );
475 msi_free( check );
476 msi_free( source );
478 return ERROR_SUCCESS;
481 static BOOL needs_ui_sequence(MSIPACKAGE *package)
483 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
486 UINT msi_set_context(MSIPACKAGE *package)
488 UINT r = msi_locate_product( package->ProductCode, &package->Context );
489 if (r != ERROR_SUCCESS)
491 int num = msi_get_property_int( package->db, szAllUsers, 0 );
492 if (num == 1 || num == 2)
493 package->Context = MSIINSTALLCONTEXT_MACHINE;
494 else
495 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
497 return ERROR_SUCCESS;
500 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
502 UINT rc;
503 LPCWSTR cond, action;
504 MSIPACKAGE *package = param;
506 action = MSI_RecordGetString(row,1);
507 if (!action)
509 ERR("Error is retrieving action name\n");
510 return ERROR_FUNCTION_FAILED;
513 /* check conditions */
514 cond = MSI_RecordGetString(row,2);
516 /* this is a hack to skip errors in the condition code */
517 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
519 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
520 return ERROR_SUCCESS;
523 if (needs_ui_sequence(package))
524 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
525 else
526 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
528 msi_dialog_check_messages( NULL );
530 if (package->CurrentInstallState != ERROR_SUCCESS)
531 rc = package->CurrentInstallState;
533 if (rc == ERROR_FUNCTION_NOT_CALLED)
534 rc = ERROR_SUCCESS;
536 if (rc != ERROR_SUCCESS)
537 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
539 if (package->need_reboot_now)
541 TRACE("action %s asked for immediate reboot, suspending installation\n",
542 debugstr_w(action));
543 rc = ACTION_ForceReboot( package );
545 return rc;
548 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
550 static const WCHAR query[] = {
551 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
552 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
553 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
554 '`','S','e','q','u','e','n','c','e','`',0};
555 MSIQUERY *view;
556 UINT r;
558 TRACE("%p %s\n", package, debugstr_w(table));
560 r = MSI_OpenQuery( package->db, &view, query, table );
561 if (r == ERROR_SUCCESS)
563 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
564 msiobj_release(&view->hdr);
566 return r;
569 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
571 static const WCHAR query[] = {
572 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
573 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
574 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
575 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
576 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
577 static const WCHAR query_validate[] = {
578 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
579 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
580 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
581 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
582 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
583 MSIQUERY *view;
584 INT seq = 0;
585 UINT rc;
587 if (package->script->ExecuteSequenceRun)
589 TRACE("Execute Sequence already Run\n");
590 return ERROR_SUCCESS;
593 package->script->ExecuteSequenceRun = TRUE;
595 /* get the sequence number */
596 if (UIran)
598 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
599 if (!row) return ERROR_FUNCTION_FAILED;
600 seq = MSI_RecordGetInteger(row,1);
601 msiobj_release(&row->hdr);
603 rc = MSI_OpenQuery(package->db, &view, query, seq);
604 if (rc == ERROR_SUCCESS)
606 TRACE("Running the actions\n");
608 msi_set_property(package->db, szSourceDir, NULL);
609 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
610 msiobj_release(&view->hdr);
612 return rc;
615 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
617 static const WCHAR query[] = {
618 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
619 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
620 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
621 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
622 MSIQUERY *view;
623 UINT rc;
625 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
626 if (rc == ERROR_SUCCESS)
628 TRACE("Running the actions\n");
629 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
630 msiobj_release(&view->hdr);
632 return rc;
635 /********************************************************
636 * ACTION helper functions and functions that perform the actions
637 *******************************************************/
638 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
639 UINT* rc, UINT script, BOOL force )
641 BOOL ret=FALSE;
642 UINT arc;
644 arc = ACTION_CustomAction(package, action, script, force);
646 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
648 *rc = arc;
649 ret = TRUE;
651 return ret;
654 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
656 MSICOMPONENT *comp;
658 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
660 if (!strcmpW( Component, comp->Component )) return comp;
662 return NULL;
665 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
667 MSIFEATURE *feature;
669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
671 if (!strcmpW( Feature, feature->Feature )) return feature;
673 return NULL;
676 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
678 MSIFILE *file;
680 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
682 if (!strcmpW( key, file->File )) return file;
684 return NULL;
687 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
689 MSIFILEPATCH *patch;
691 /* FIXME: There might be more than one patch */
692 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
694 if (!strcmpW( key, patch->File->File )) return patch;
696 return NULL;
699 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
701 MSIFOLDER *folder;
703 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
705 if (!strcmpW( dir, folder->Directory )) return folder;
707 return NULL;
711 * Recursively create all directories in the path.
712 * shamelessly stolen from setupapi/queue.c
714 BOOL msi_create_full_path( const WCHAR *path )
716 BOOL ret = TRUE;
717 WCHAR *new_path;
718 int len;
720 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
721 strcpyW( new_path, path );
723 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
724 new_path[len - 1] = 0;
726 while (!CreateDirectoryW( new_path, NULL ))
728 WCHAR *slash;
729 DWORD last_error = GetLastError();
730 if (last_error == ERROR_ALREADY_EXISTS) break;
731 if (last_error != ERROR_PATH_NOT_FOUND)
733 ret = FALSE;
734 break;
736 if (!(slash = strrchrW( new_path, '\\' )))
738 ret = FALSE;
739 break;
741 len = slash - new_path;
742 new_path[len] = 0;
743 if (!msi_create_full_path( new_path ))
745 ret = FALSE;
746 break;
748 new_path[len] = '\\';
750 msi_free( new_path );
751 return ret;
754 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
756 MSIRECORD *row;
758 row = MSI_CreateRecord( 4 );
759 MSI_RecordSetInteger( row, 1, a );
760 MSI_RecordSetInteger( row, 2, b );
761 MSI_RecordSetInteger( row, 3, c );
762 MSI_RecordSetInteger( row, 4, d );
763 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
764 msiobj_release( &row->hdr );
766 msi_dialog_check_messages( NULL );
769 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
771 static const WCHAR query[] =
772 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
773 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
774 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
775 WCHAR message[1024];
776 MSIRECORD *row = 0;
777 DWORD size;
779 if (!package->LastAction || strcmpW( package->LastAction, action ))
781 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
783 if (MSI_RecordIsNull( row, 3 ))
785 msiobj_release( &row->hdr );
786 return;
788 /* update the cached action format */
789 msi_free( package->ActionFormat );
790 package->ActionFormat = msi_dup_record_field( row, 3 );
791 msi_free( package->LastAction );
792 package->LastAction = strdupW( action );
793 msiobj_release( &row->hdr );
795 size = 1024;
796 MSI_RecordSetStringW( record, 0, package->ActionFormat );
797 MSI_FormatRecordW( package, record, message, &size );
798 row = MSI_CreateRecord( 1 );
799 MSI_RecordSetStringW( row, 1, message );
800 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
801 msiobj_release( &row->hdr );
804 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
806 if (!comp->Enabled)
808 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
809 return INSTALLSTATE_UNKNOWN;
811 if (package->need_rollback) return comp->Installed;
812 return comp->ActionRequest;
815 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
817 if (package->need_rollback) return feature->Installed;
818 return feature->ActionRequest;
821 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
823 MSIPACKAGE *package = param;
824 LPCWSTR dir, component, full_path;
825 MSIRECORD *uirow;
826 MSIFOLDER *folder;
827 MSICOMPONENT *comp;
829 component = MSI_RecordGetString(row, 2);
830 if (!component)
831 return ERROR_SUCCESS;
833 comp = msi_get_loaded_component(package, component);
834 if (!comp)
835 return ERROR_SUCCESS;
837 comp->Action = msi_get_component_action( package, comp );
838 if (comp->Action != INSTALLSTATE_LOCAL)
840 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
841 return ERROR_SUCCESS;
844 dir = MSI_RecordGetString(row,1);
845 if (!dir)
847 ERR("Unable to get folder id\n");
848 return ERROR_SUCCESS;
851 uirow = MSI_CreateRecord(1);
852 MSI_RecordSetStringW(uirow, 1, dir);
853 msi_ui_actiondata(package, szCreateFolders, uirow);
854 msiobj_release(&uirow->hdr);
856 full_path = msi_get_target_folder( package, dir );
857 if (!full_path)
859 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
860 return ERROR_SUCCESS;
862 TRACE("folder is %s\n", debugstr_w(full_path));
864 folder = msi_get_loaded_folder( package, dir );
865 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
866 folder->State = FOLDER_STATE_CREATED;
867 return ERROR_SUCCESS;
870 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
872 static const WCHAR query[] = {
873 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
874 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
875 MSIQUERY *view;
876 UINT rc;
878 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
879 if (rc != ERROR_SUCCESS)
880 return ERROR_SUCCESS;
882 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
883 msiobj_release(&view->hdr);
884 return rc;
887 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
889 MSIPACKAGE *package = param;
890 LPCWSTR dir, component, full_path;
891 MSIRECORD *uirow;
892 MSIFOLDER *folder;
893 MSICOMPONENT *comp;
895 component = MSI_RecordGetString(row, 2);
896 if (!component)
897 return ERROR_SUCCESS;
899 comp = msi_get_loaded_component(package, component);
900 if (!comp)
901 return ERROR_SUCCESS;
903 comp->Action = msi_get_component_action( package, comp );
904 if (comp->Action != INSTALLSTATE_ABSENT)
906 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
907 return ERROR_SUCCESS;
910 dir = MSI_RecordGetString( row, 1 );
911 if (!dir)
913 ERR("Unable to get folder id\n");
914 return ERROR_SUCCESS;
917 full_path = msi_get_target_folder( package, dir );
918 if (!full_path)
920 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
921 return ERROR_SUCCESS;
923 TRACE("folder is %s\n", debugstr_w(full_path));
925 uirow = MSI_CreateRecord( 1 );
926 MSI_RecordSetStringW( uirow, 1, dir );
927 msi_ui_actiondata( package, szRemoveFolders, uirow );
928 msiobj_release( &uirow->hdr );
930 RemoveDirectoryW( full_path );
931 folder = msi_get_loaded_folder( package, dir );
932 folder->State = FOLDER_STATE_REMOVED;
933 return ERROR_SUCCESS;
936 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
938 static const WCHAR query[] = {
939 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
940 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
941 MSIQUERY *view;
942 UINT rc;
944 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
945 if (rc != ERROR_SUCCESS)
946 return ERROR_SUCCESS;
948 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
949 msiobj_release( &view->hdr );
950 return rc;
953 static UINT load_component( MSIRECORD *row, LPVOID param )
955 MSIPACKAGE *package = param;
956 MSICOMPONENT *comp;
958 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
959 if (!comp)
960 return ERROR_FUNCTION_FAILED;
962 list_add_tail( &package->components, &comp->entry );
964 /* fill in the data */
965 comp->Component = msi_dup_record_field( row, 1 );
967 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
969 comp->ComponentId = msi_dup_record_field( row, 2 );
970 comp->Directory = msi_dup_record_field( row, 3 );
971 comp->Attributes = MSI_RecordGetInteger(row,4);
972 comp->Condition = msi_dup_record_field( row, 5 );
973 comp->KeyPath = msi_dup_record_field( row, 6 );
975 comp->Installed = INSTALLSTATE_UNKNOWN;
976 comp->Action = INSTALLSTATE_UNKNOWN;
977 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
979 comp->assembly = msi_load_assembly( package, comp );
980 return ERROR_SUCCESS;
983 UINT msi_load_all_components( MSIPACKAGE *package )
985 static const WCHAR query[] = {
986 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
987 '`','C','o','m','p','o','n','e','n','t','`',0};
988 MSIQUERY *view;
989 UINT r;
991 if (!list_empty(&package->components))
992 return ERROR_SUCCESS;
994 r = MSI_DatabaseOpenViewW( package->db, query, &view );
995 if (r != ERROR_SUCCESS)
996 return r;
998 if (!msi_init_assembly_caches( package ))
1000 ERR("can't initialize assembly caches\n");
1001 msiobj_release( &view->hdr );
1002 return ERROR_FUNCTION_FAILED;
1005 r = MSI_IterateRecords(view, NULL, load_component, package);
1006 msiobj_release(&view->hdr);
1007 msi_destroy_assembly_caches( package );
1008 return r;
1011 typedef struct {
1012 MSIPACKAGE *package;
1013 MSIFEATURE *feature;
1014 } _ilfs;
1016 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1018 ComponentList *cl;
1020 cl = msi_alloc( sizeof (*cl) );
1021 if ( !cl )
1022 return ERROR_NOT_ENOUGH_MEMORY;
1023 cl->component = comp;
1024 list_add_tail( &feature->Components, &cl->entry );
1026 return ERROR_SUCCESS;
1029 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1031 FeatureList *fl;
1033 fl = msi_alloc( sizeof(*fl) );
1034 if ( !fl )
1035 return ERROR_NOT_ENOUGH_MEMORY;
1036 fl->feature = child;
1037 list_add_tail( &parent->Children, &fl->entry );
1039 return ERROR_SUCCESS;
1042 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1044 _ilfs* ilfs = param;
1045 LPCWSTR component;
1046 MSICOMPONENT *comp;
1048 component = MSI_RecordGetString(row,1);
1050 /* check to see if the component is already loaded */
1051 comp = msi_get_loaded_component( ilfs->package, component );
1052 if (!comp)
1054 WARN("ignoring unknown component %s\n", debugstr_w(component));
1055 return ERROR_SUCCESS;
1057 add_feature_component( ilfs->feature, comp );
1058 comp->Enabled = TRUE;
1060 return ERROR_SUCCESS;
1063 static UINT load_feature(MSIRECORD * row, LPVOID param)
1065 static const WCHAR query[] = {
1066 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1067 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1068 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1069 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1070 MSIPACKAGE *package = param;
1071 MSIFEATURE *feature;
1072 MSIQUERY *view;
1073 _ilfs ilfs;
1074 UINT rc;
1076 /* fill in the data */
1078 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1079 if (!feature)
1080 return ERROR_NOT_ENOUGH_MEMORY;
1082 list_init( &feature->Children );
1083 list_init( &feature->Components );
1085 feature->Feature = msi_dup_record_field( row, 1 );
1087 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1089 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1090 feature->Title = msi_dup_record_field( row, 3 );
1091 feature->Description = msi_dup_record_field( row, 4 );
1093 if (!MSI_RecordIsNull(row,5))
1094 feature->Display = MSI_RecordGetInteger(row,5);
1096 feature->Level= MSI_RecordGetInteger(row,6);
1097 feature->Directory = msi_dup_record_field( row, 7 );
1098 feature->Attributes = MSI_RecordGetInteger(row,8);
1100 feature->Installed = INSTALLSTATE_UNKNOWN;
1101 feature->Action = INSTALLSTATE_UNKNOWN;
1102 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1104 list_add_tail( &package->features, &feature->entry );
1106 /* load feature components */
1108 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1109 if (rc != ERROR_SUCCESS)
1110 return ERROR_SUCCESS;
1112 ilfs.package = package;
1113 ilfs.feature = feature;
1115 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1116 msiobj_release(&view->hdr);
1117 return rc;
1120 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1122 MSIPACKAGE *package = param;
1123 MSIFEATURE *parent, *child;
1125 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1126 if (!child)
1127 return ERROR_FUNCTION_FAILED;
1129 if (!child->Feature_Parent)
1130 return ERROR_SUCCESS;
1132 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1133 if (!parent)
1134 return ERROR_FUNCTION_FAILED;
1136 add_feature_child( parent, child );
1137 return ERROR_SUCCESS;
1140 UINT msi_load_all_features( MSIPACKAGE *package )
1142 static const WCHAR query[] = {
1143 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1144 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1145 '`','D','i','s','p','l','a','y','`',0};
1146 MSIQUERY *view;
1147 UINT r;
1149 if (!list_empty(&package->features))
1150 return ERROR_SUCCESS;
1152 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1153 if (r != ERROR_SUCCESS)
1154 return r;
1156 r = MSI_IterateRecords( view, NULL, load_feature, package );
1157 if (r != ERROR_SUCCESS)
1159 msiobj_release( &view->hdr );
1160 return r;
1162 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1163 msiobj_release( &view->hdr );
1164 return r;
1167 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1169 if (!p)
1170 return p;
1171 p = strchrW(p, ch);
1172 if (!p)
1173 return p;
1174 *p = 0;
1175 return p+1;
1178 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1180 static const WCHAR query[] = {
1181 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1182 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1183 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1184 MSIQUERY *view = NULL;
1185 MSIRECORD *row = NULL;
1186 UINT r;
1188 TRACE("%s\n", debugstr_w(file->File));
1190 r = MSI_OpenQuery(package->db, &view, query, file->File);
1191 if (r != ERROR_SUCCESS)
1192 goto done;
1194 r = MSI_ViewExecute(view, NULL);
1195 if (r != ERROR_SUCCESS)
1196 goto done;
1198 r = MSI_ViewFetch(view, &row);
1199 if (r != ERROR_SUCCESS)
1200 goto done;
1202 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1203 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1204 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1205 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1206 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1208 done:
1209 if (view) msiobj_release(&view->hdr);
1210 if (row) msiobj_release(&row->hdr);
1211 return r;
1214 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1216 MSIRECORD *row;
1217 static const WCHAR query[] = {
1218 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1219 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1220 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1222 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1223 if (!row)
1225 WARN("query failed\n");
1226 return ERROR_FUNCTION_FAILED;
1229 file->disk_id = MSI_RecordGetInteger( row, 1 );
1230 msiobj_release( &row->hdr );
1231 return ERROR_SUCCESS;
1234 static UINT load_file(MSIRECORD *row, LPVOID param)
1236 MSIPACKAGE* package = param;
1237 LPCWSTR component;
1238 MSIFILE *file;
1240 /* fill in the data */
1242 file = msi_alloc_zero( sizeof (MSIFILE) );
1243 if (!file)
1244 return ERROR_NOT_ENOUGH_MEMORY;
1246 file->File = msi_dup_record_field( row, 1 );
1248 component = MSI_RecordGetString( row, 2 );
1249 file->Component = msi_get_loaded_component( package, component );
1251 if (!file->Component)
1253 WARN("Component not found: %s\n", debugstr_w(component));
1254 msi_free(file->File);
1255 msi_free(file);
1256 return ERROR_SUCCESS;
1259 file->FileName = msi_dup_record_field( row, 3 );
1260 msi_reduce_to_long_filename( file->FileName );
1262 file->ShortName = msi_dup_record_field( row, 3 );
1263 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1265 file->FileSize = MSI_RecordGetInteger( row, 4 );
1266 file->Version = msi_dup_record_field( row, 5 );
1267 file->Language = msi_dup_record_field( row, 6 );
1268 file->Attributes = MSI_RecordGetInteger( row, 7 );
1269 file->Sequence = MSI_RecordGetInteger( row, 8 );
1271 file->state = msifs_invalid;
1273 /* if the compressed bits are not set in the file attributes,
1274 * then read the information from the package word count property
1276 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1278 file->IsCompressed = FALSE;
1280 else if (file->Attributes &
1281 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1283 file->IsCompressed = TRUE;
1285 else if (file->Attributes & msidbFileAttributesNoncompressed)
1287 file->IsCompressed = FALSE;
1289 else
1291 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1294 load_file_hash(package, file);
1295 load_file_disk_id(package, file);
1297 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1299 list_add_tail( &package->files, &file->entry );
1301 return ERROR_SUCCESS;
1304 static UINT load_all_files(MSIPACKAGE *package)
1306 static const WCHAR query[] = {
1307 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1308 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1309 '`','S','e','q','u','e','n','c','e','`', 0};
1310 MSIQUERY *view;
1311 UINT rc;
1313 if (!list_empty(&package->files))
1314 return ERROR_SUCCESS;
1316 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1317 if (rc != ERROR_SUCCESS)
1318 return ERROR_SUCCESS;
1320 rc = MSI_IterateRecords(view, NULL, load_file, package);
1321 msiobj_release(&view->hdr);
1322 return rc;
1325 static UINT load_media( MSIRECORD *row, LPVOID param )
1327 MSIPACKAGE *package = param;
1328 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1329 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1331 /* FIXME: load external cabinets and directory sources too */
1332 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1333 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1334 return ERROR_SUCCESS;
1337 static UINT load_all_media( MSIPACKAGE *package )
1339 static const WCHAR query[] = {
1340 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1341 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1342 '`','D','i','s','k','I','d','`',0};
1343 MSIQUERY *view;
1344 UINT r;
1346 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1347 if (r != ERROR_SUCCESS)
1348 return ERROR_SUCCESS;
1350 r = MSI_IterateRecords( view, NULL, load_media, package );
1351 msiobj_release( &view->hdr );
1352 return r;
1355 static UINT load_patch(MSIRECORD *row, LPVOID param)
1357 MSIPACKAGE *package = param;
1358 MSIFILEPATCH *patch;
1359 LPWSTR file_key;
1361 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1362 if (!patch)
1363 return ERROR_NOT_ENOUGH_MEMORY;
1365 file_key = msi_dup_record_field( row, 1 );
1366 patch->File = msi_get_loaded_file( package, file_key );
1367 msi_free(file_key);
1369 if( !patch->File )
1371 ERR("Failed to find target for patch in File table\n");
1372 msi_free(patch);
1373 return ERROR_FUNCTION_FAILED;
1376 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1378 /* FIXME: The database should be properly transformed */
1379 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1381 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1382 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1383 patch->IsApplied = FALSE;
1385 /* FIXME:
1386 * Header field - for patch validation.
1387 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1390 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1392 list_add_tail( &package->filepatches, &patch->entry );
1394 return ERROR_SUCCESS;
1397 static UINT load_all_patches(MSIPACKAGE *package)
1399 static const WCHAR query[] = {
1400 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1401 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1402 '`','S','e','q','u','e','n','c','e','`',0};
1403 MSIQUERY *view;
1404 UINT rc;
1406 if (!list_empty(&package->filepatches))
1407 return ERROR_SUCCESS;
1409 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1410 if (rc != ERROR_SUCCESS)
1411 return ERROR_SUCCESS;
1413 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1414 msiobj_release(&view->hdr);
1415 return rc;
1418 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1420 static const WCHAR query[] = {
1421 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1422 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1423 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1424 MSIQUERY *view;
1426 folder->persistent = FALSE;
1427 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1429 if (!MSI_ViewExecute( view, NULL ))
1431 MSIRECORD *rec;
1432 if (!MSI_ViewFetch( view, &rec ))
1434 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1435 folder->persistent = TRUE;
1436 msiobj_release( &rec->hdr );
1439 msiobj_release( &view->hdr );
1441 return ERROR_SUCCESS;
1444 static UINT load_folder( MSIRECORD *row, LPVOID param )
1446 MSIPACKAGE *package = param;
1447 static WCHAR szEmpty[] = { 0 };
1448 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1449 MSIFOLDER *folder;
1451 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1452 list_init( &folder->children );
1453 folder->Directory = msi_dup_record_field( row, 1 );
1454 folder->Parent = msi_dup_record_field( row, 2 );
1455 p = msi_dup_record_field(row, 3);
1457 TRACE("%s\n", debugstr_w(folder->Directory));
1459 /* split src and target dir */
1460 tgt_short = p;
1461 src_short = folder_split_path( p, ':' );
1463 /* split the long and short paths */
1464 tgt_long = folder_split_path( tgt_short, '|' );
1465 src_long = folder_split_path( src_short, '|' );
1467 /* check for no-op dirs */
1468 if (tgt_short && !strcmpW( szDot, tgt_short ))
1469 tgt_short = szEmpty;
1470 if (src_short && !strcmpW( szDot, src_short ))
1471 src_short = szEmpty;
1473 if (!tgt_long)
1474 tgt_long = tgt_short;
1476 if (!src_short) {
1477 src_short = tgt_short;
1478 src_long = tgt_long;
1481 if (!src_long)
1482 src_long = src_short;
1484 /* FIXME: use the target short path too */
1485 folder->TargetDefault = strdupW(tgt_long);
1486 folder->SourceShortPath = strdupW(src_short);
1487 folder->SourceLongPath = strdupW(src_long);
1488 msi_free(p);
1490 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1491 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1492 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1494 load_folder_persistence( package, folder );
1496 list_add_tail( &package->folders, &folder->entry );
1497 return ERROR_SUCCESS;
1500 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1502 FolderList *fl;
1504 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1505 fl->folder = child;
1506 list_add_tail( &parent->children, &fl->entry );
1507 return ERROR_SUCCESS;
1510 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1512 MSIPACKAGE *package = param;
1513 MSIFOLDER *parent, *child;
1515 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1516 return ERROR_FUNCTION_FAILED;
1518 if (!child->Parent) return ERROR_SUCCESS;
1520 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1521 return ERROR_FUNCTION_FAILED;
1523 return add_folder_child( parent, child );
1526 static UINT load_all_folders( MSIPACKAGE *package )
1528 static const WCHAR query[] = {
1529 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1530 '`','D','i','r','e','c','t','o','r','y','`',0};
1531 MSIQUERY *view;
1532 UINT r;
1534 if (!list_empty(&package->folders))
1535 return ERROR_SUCCESS;
1537 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1538 if (r != ERROR_SUCCESS)
1539 return r;
1541 r = MSI_IterateRecords( view, NULL, load_folder, package );
1542 if (r != ERROR_SUCCESS)
1544 msiobj_release( &view->hdr );
1545 return r;
1547 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1548 msiobj_release( &view->hdr );
1549 return r;
1552 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1554 msi_set_property( package->db, szCostingComplete, szZero );
1555 msi_set_property( package->db, szRootDrive, szCRoot );
1557 load_all_folders( package );
1558 msi_load_all_components( package );
1559 msi_load_all_features( package );
1560 load_all_files( package );
1561 load_all_patches( package );
1562 load_all_media( package );
1564 return ERROR_SUCCESS;
1567 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1569 const WCHAR *action = package->script->Actions[script][index];
1570 ui_actionstart( package, action );
1571 TRACE("executing %s\n", debugstr_w(action));
1572 return ACTION_PerformAction( package, action, script );
1575 static UINT execute_script( MSIPACKAGE *package, UINT script )
1577 UINT i, rc = ERROR_SUCCESS;
1579 TRACE("executing script %u\n", script);
1581 if (!package->script)
1583 ERR("no script!\n");
1584 return ERROR_FUNCTION_FAILED;
1586 if (script == SCRIPT_ROLLBACK)
1588 for (i = package->script->ActionCount[script]; i > 0; i--)
1590 rc = execute_script_action( package, script, i - 1 );
1591 if (rc != ERROR_SUCCESS) break;
1594 else
1596 for (i = 0; i < package->script->ActionCount[script]; i++)
1598 rc = execute_script_action( package, script, i );
1599 if (rc != ERROR_SUCCESS) break;
1602 msi_free_action_script(package, script);
1603 return rc;
1606 static UINT ACTION_FileCost(MSIPACKAGE *package)
1608 return ERROR_SUCCESS;
1611 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1613 MSICOMPONENT *comp;
1614 UINT r;
1616 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1618 if (!comp->ComponentId) continue;
1620 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1621 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1622 &comp->Installed );
1623 if (r != ERROR_SUCCESS)
1624 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1625 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1626 &comp->Installed );
1627 if (r != ERROR_SUCCESS)
1628 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1629 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1630 &comp->Installed );
1631 if (r != ERROR_SUCCESS)
1632 comp->Installed = INSTALLSTATE_ABSENT;
1636 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1638 MSIFEATURE *feature;
1640 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1642 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1644 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1645 feature->Installed = INSTALLSTATE_ABSENT;
1646 else
1647 feature->Installed = state;
1651 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1653 return (feature->Level > 0 && feature->Level <= level);
1656 static BOOL process_state_property(MSIPACKAGE* package, int level,
1657 LPCWSTR property, INSTALLSTATE state)
1659 LPWSTR override;
1660 MSIFEATURE *feature;
1662 override = msi_dup_property( package->db, property );
1663 if (!override)
1664 return FALSE;
1666 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1668 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1669 continue;
1671 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1673 if (!strcmpiW( override, szAll ))
1675 if (feature->Installed != state)
1677 feature->Action = state;
1678 feature->ActionRequest = state;
1681 else
1683 LPWSTR ptr = override;
1684 LPWSTR ptr2 = strchrW(override,',');
1686 while (ptr)
1688 int len = ptr2 - ptr;
1690 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1691 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1693 if (feature->Installed != state)
1695 feature->Action = state;
1696 feature->ActionRequest = state;
1698 break;
1700 if (ptr2)
1702 ptr=ptr2+1;
1703 ptr2 = strchrW(ptr,',');
1705 else
1706 break;
1710 msi_free(override);
1711 return TRUE;
1714 static BOOL process_overrides( MSIPACKAGE *package, int level )
1716 static const WCHAR szAddLocal[] =
1717 {'A','D','D','L','O','C','A','L',0};
1718 static const WCHAR szAddSource[] =
1719 {'A','D','D','S','O','U','R','C','E',0};
1720 static const WCHAR szAdvertise[] =
1721 {'A','D','V','E','R','T','I','S','E',0};
1722 BOOL ret = FALSE;
1724 /* all these activation/deactivation things happen in order and things
1725 * later on the list override things earlier on the list.
1727 * 0 INSTALLLEVEL processing
1728 * 1 ADDLOCAL
1729 * 2 REMOVE
1730 * 3 ADDSOURCE
1731 * 4 ADDDEFAULT
1732 * 5 REINSTALL
1733 * 6 ADVERTISE
1734 * 7 COMPADDLOCAL
1735 * 8 COMPADDSOURCE
1736 * 9 FILEADDLOCAL
1737 * 10 FILEADDSOURCE
1738 * 11 FILEADDDEFAULT
1740 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1741 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1742 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1743 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1744 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1746 if (ret)
1747 msi_set_property( package->db, szPreselected, szOne );
1749 return ret;
1752 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1754 int level;
1755 MSICOMPONENT* component;
1756 MSIFEATURE *feature;
1758 TRACE("Checking Install Level\n");
1760 level = msi_get_property_int(package->db, szInstallLevel, 1);
1762 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1764 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1766 if (!is_feature_selected( feature, level )) continue;
1768 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1770 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1772 feature->Action = INSTALLSTATE_SOURCE;
1773 feature->ActionRequest = INSTALLSTATE_SOURCE;
1775 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1777 feature->Action = INSTALLSTATE_ADVERTISED;
1778 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1780 else
1782 feature->Action = INSTALLSTATE_LOCAL;
1783 feature->ActionRequest = INSTALLSTATE_LOCAL;
1787 /* disable child features of unselected parent or follow parent */
1788 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1790 FeatureList *fl;
1792 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1794 if (!is_feature_selected( feature, level ))
1796 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1797 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1799 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1801 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1802 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1803 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1804 fl->feature->Action = feature->Action;
1805 fl->feature->ActionRequest = feature->ActionRequest;
1810 else /* preselected */
1812 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1814 if (!is_feature_selected( feature, level )) continue;
1816 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1818 if (feature->Installed == INSTALLSTATE_ABSENT)
1820 feature->Action = INSTALLSTATE_UNKNOWN;
1821 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1823 else
1825 feature->Action = feature->Installed;
1826 feature->ActionRequest = feature->Installed;
1830 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1832 FeatureList *fl;
1834 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1836 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1837 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1839 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1840 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1841 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1842 fl->feature->Action = feature->Action;
1843 fl->feature->ActionRequest = feature->ActionRequest;
1849 /* now we want to set component state based based on feature state */
1850 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1852 ComponentList *cl;
1854 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1855 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1856 feature->ActionRequest, feature->Action);
1858 if (!is_feature_selected( feature, level )) continue;
1860 /* features with components that have compressed files are made local */
1861 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1863 if (cl->component->ForceLocalState &&
1864 feature->ActionRequest == INSTALLSTATE_SOURCE)
1866 feature->Action = INSTALLSTATE_LOCAL;
1867 feature->ActionRequest = INSTALLSTATE_LOCAL;
1868 break;
1872 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1874 component = cl->component;
1876 switch (feature->ActionRequest)
1878 case INSTALLSTATE_ABSENT:
1879 component->anyAbsent = 1;
1880 break;
1881 case INSTALLSTATE_ADVERTISED:
1882 component->hasAdvertiseFeature = 1;
1883 break;
1884 case INSTALLSTATE_SOURCE:
1885 component->hasSourceFeature = 1;
1886 break;
1887 case INSTALLSTATE_LOCAL:
1888 component->hasLocalFeature = 1;
1889 break;
1890 case INSTALLSTATE_DEFAULT:
1891 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1892 component->hasAdvertiseFeature = 1;
1893 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1894 component->hasSourceFeature = 1;
1895 else
1896 component->hasLocalFeature = 1;
1897 break;
1898 default:
1899 break;
1904 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1906 /* check if it's local or source */
1907 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1908 (component->hasLocalFeature || component->hasSourceFeature))
1910 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1911 !component->ForceLocalState)
1913 component->Action = INSTALLSTATE_SOURCE;
1914 component->ActionRequest = INSTALLSTATE_SOURCE;
1916 else
1918 component->Action = INSTALLSTATE_LOCAL;
1919 component->ActionRequest = INSTALLSTATE_LOCAL;
1921 continue;
1924 /* if any feature is local, the component must be local too */
1925 if (component->hasLocalFeature)
1927 component->Action = INSTALLSTATE_LOCAL;
1928 component->ActionRequest = INSTALLSTATE_LOCAL;
1929 continue;
1931 if (component->hasSourceFeature)
1933 component->Action = INSTALLSTATE_SOURCE;
1934 component->ActionRequest = INSTALLSTATE_SOURCE;
1935 continue;
1937 if (component->hasAdvertiseFeature)
1939 component->Action = INSTALLSTATE_ADVERTISED;
1940 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1941 continue;
1943 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1944 if (component->anyAbsent &&
1945 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1947 component->Action = INSTALLSTATE_ABSENT;
1948 component->ActionRequest = INSTALLSTATE_ABSENT;
1952 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1954 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1956 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1957 component->Action = INSTALLSTATE_LOCAL;
1958 component->ActionRequest = INSTALLSTATE_LOCAL;
1961 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1962 component->Installed == INSTALLSTATE_SOURCE &&
1963 component->hasSourceFeature)
1965 component->Action = INSTALLSTATE_UNKNOWN;
1966 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1969 TRACE("component %s (installed %d request %d action %d)\n",
1970 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1973 return ERROR_SUCCESS;
1976 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1978 MSIPACKAGE *package = param;
1979 LPCWSTR name;
1980 MSIFEATURE *feature;
1982 name = MSI_RecordGetString( row, 1 );
1984 feature = msi_get_loaded_feature( package, name );
1985 if (!feature)
1986 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
1987 else
1989 LPCWSTR Condition;
1990 Condition = MSI_RecordGetString(row,3);
1992 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
1994 int level = MSI_RecordGetInteger(row,2);
1995 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
1996 feature->Level = level;
1999 return ERROR_SUCCESS;
2002 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2004 static const WCHAR name[] = {'\\',0};
2005 VS_FIXEDFILEINFO *ptr, *ret;
2006 LPVOID version;
2007 DWORD versize, handle;
2008 UINT sz;
2010 versize = GetFileVersionInfoSizeW( filename, &handle );
2011 if (!versize)
2012 return NULL;
2014 version = msi_alloc( versize );
2015 if (!version)
2016 return NULL;
2018 GetFileVersionInfoW( filename, 0, versize, version );
2020 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2022 msi_free( version );
2023 return NULL;
2026 ret = msi_alloc( sz );
2027 memcpy( ret, ptr, sz );
2029 msi_free( version );
2030 return ret;
2033 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2035 DWORD ms, ls;
2037 msi_parse_version_string( version, &ms, &ls );
2039 if (fi->dwFileVersionMS > ms) return 1;
2040 else if (fi->dwFileVersionMS < ms) return -1;
2041 else if (fi->dwFileVersionLS > ls) return 1;
2042 else if (fi->dwFileVersionLS < ls) return -1;
2043 return 0;
2046 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2048 DWORD ms1, ms2;
2050 msi_parse_version_string( ver1, &ms1, NULL );
2051 msi_parse_version_string( ver2, &ms2, NULL );
2053 if (ms1 > ms2) return 1;
2054 else if (ms1 < ms2) return -1;
2055 return 0;
2058 DWORD msi_get_disk_file_size( LPCWSTR filename )
2060 HANDLE file;
2061 DWORD size;
2063 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2064 if (file == INVALID_HANDLE_VALUE)
2065 return INVALID_FILE_SIZE;
2067 size = GetFileSize( file, NULL );
2068 TRACE("size is %u\n", size);
2069 CloseHandle( file );
2070 return size;
2073 BOOL msi_file_hash_matches( MSIFILE *file )
2075 UINT r;
2076 MSIFILEHASHINFO hash;
2078 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2079 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2080 if (r != ERROR_SUCCESS)
2081 return FALSE;
2083 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2086 static WCHAR *get_temp_dir( void )
2088 static UINT id;
2089 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2091 GetTempPathW( MAX_PATH, tmp );
2092 for (;;)
2094 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2095 if (CreateDirectoryW( dir, NULL )) break;
2097 return strdupW( dir );
2101 * msi_build_directory_name()
2103 * This function is to save messing round with directory names
2104 * It handles adding backslashes between path segments,
2105 * and can add \ at the end of the directory name if told to.
2107 * It takes a variable number of arguments.
2108 * It always allocates a new string for the result, so make sure
2109 * to free the return value when finished with it.
2111 * The first arg is the number of path segments that follow.
2112 * The arguments following count are a list of path segments.
2113 * A path segment may be NULL.
2115 * Path segments will be added with a \ separating them.
2116 * A \ will not be added after the last segment, however if the
2117 * last segment is NULL, then the last character will be a \
2119 WCHAR *msi_build_directory_name( DWORD count, ... )
2121 DWORD sz = 1, i;
2122 WCHAR *dir;
2123 va_list va;
2125 va_start( va, count );
2126 for (i = 0; i < count; i++)
2128 const WCHAR *str = va_arg( va, const WCHAR * );
2129 if (str) sz += strlenW( str ) + 1;
2131 va_end( va );
2133 dir = msi_alloc( sz * sizeof(WCHAR) );
2134 dir[0] = 0;
2136 va_start( va, count );
2137 for (i = 0; i < count; i++)
2139 const WCHAR *str = va_arg( va, const WCHAR * );
2140 if (!str) continue;
2141 strcatW( dir, str );
2142 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2144 va_end( va );
2145 return dir;
2148 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2150 MSIASSEMBLY *assembly = file->Component->assembly;
2152 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2154 msi_free( file->TargetPath );
2155 if (assembly && !assembly->application)
2157 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2158 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2159 msi_track_tempfile( package, file->TargetPath );
2161 else
2163 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2164 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2167 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2170 static UINT calculate_file_cost( MSIPACKAGE *package )
2172 VS_FIXEDFILEINFO *file_version;
2173 WCHAR *font_version;
2174 MSIFILE *file;
2176 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2178 MSICOMPONENT *comp = file->Component;
2179 DWORD file_size;
2181 if (!comp->Enabled) continue;
2183 if (file->IsCompressed)
2184 comp->ForceLocalState = TRUE;
2186 set_target_path( package, file );
2188 if ((comp->assembly && !comp->assembly->installed) ||
2189 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2191 comp->Cost += file->FileSize;
2192 continue;
2194 file_size = msi_get_disk_file_size( file->TargetPath );
2196 if (file->Version)
2198 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2200 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2202 comp->Cost += file->FileSize - file_size;
2204 msi_free( file_version );
2205 continue;
2207 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2209 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2211 comp->Cost += file->FileSize - file_size;
2213 msi_free( font_version );
2214 continue;
2217 if (file_size != file->FileSize)
2219 comp->Cost += file->FileSize - file_size;
2222 return ERROR_SUCCESS;
2225 WCHAR *msi_normalize_path( const WCHAR *in )
2227 const WCHAR *p = in;
2228 WCHAR *q, *ret;
2229 int n, len = strlenW( in ) + 2;
2231 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2233 len = 0;
2234 while (1)
2236 /* copy until the end of the string or a space */
2237 while (*p != ' ' && (*q = *p))
2239 p++, len++;
2240 /* reduce many backslashes to one */
2241 if (*p != '\\' || *q != '\\')
2242 q++;
2245 /* quit at the end of the string */
2246 if (!*p)
2247 break;
2249 /* count the number of spaces */
2250 n = 0;
2251 while (p[n] == ' ')
2252 n++;
2254 /* if it's leading or trailing space, skip it */
2255 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2256 p += n;
2257 else /* copy n spaces */
2258 while (n && (*q++ = *p++)) n--;
2260 while (q - ret > 0 && q[-1] == ' ') q--;
2261 if (q - ret > 0 && q[-1] != '\\')
2263 q[0] = '\\';
2264 q[1] = 0;
2266 return ret;
2269 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2271 FolderList *fl;
2272 MSIFOLDER *folder, *parent, *child;
2273 WCHAR *path, *normalized_path;
2275 TRACE("resolving %s\n", debugstr_w(name));
2277 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2279 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2281 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
2283 path = msi_dup_property( package->db, szRootDrive );
2286 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2288 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2290 parent = msi_get_loaded_folder( package, folder->Parent );
2291 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2293 else
2294 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2296 normalized_path = msi_normalize_path( path );
2297 msi_free( path );
2298 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2300 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2301 msi_free( normalized_path );
2302 return;
2304 msi_set_property( package->db, folder->Directory, normalized_path );
2305 msi_free( folder->ResolvedTarget );
2306 folder->ResolvedTarget = normalized_path;
2308 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2310 child = fl->folder;
2311 msi_resolve_target_folder( package, child->Directory, load_prop );
2313 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2316 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2318 static const WCHAR query[] = {
2319 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2320 '`','C','o','n','d','i','t','i','o','n','`',0};
2321 static const WCHAR szOutOfDiskSpace[] = {
2322 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2323 MSICOMPONENT *comp;
2324 MSIQUERY *view;
2325 LPWSTR level;
2326 UINT rc;
2328 TRACE("Building directory properties\n");
2329 msi_resolve_target_folder( package, szTargetDir, TRUE );
2331 TRACE("Evaluating component conditions\n");
2332 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2334 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2336 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2337 comp->Enabled = FALSE;
2339 else
2340 comp->Enabled = TRUE;
2343 /* read components states from the registry */
2344 ACTION_GetComponentInstallStates(package);
2345 ACTION_GetFeatureInstallStates(package);
2347 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2349 TRACE("Evaluating feature conditions\n");
2351 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2352 if (rc == ERROR_SUCCESS)
2354 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2355 msiobj_release( &view->hdr );
2356 if (rc != ERROR_SUCCESS)
2357 return rc;
2361 TRACE("Calculating file cost\n");
2362 calculate_file_cost( package );
2364 msi_set_property( package->db, szCostingComplete, szOne );
2365 /* set default run level if not set */
2366 level = msi_dup_property( package->db, szInstallLevel );
2367 if (!level)
2368 msi_set_property( package->db, szInstallLevel, szOne );
2369 msi_free(level);
2371 /* FIXME: check volume disk space */
2372 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2374 return MSI_SetFeatureStates(package);
2377 /* OK this value is "interpreted" and then formatted based on the
2378 first few characters */
2379 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2380 DWORD *size)
2382 LPSTR data = NULL;
2384 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2386 if (value[1]=='x')
2388 LPWSTR ptr;
2389 CHAR byte[5];
2390 LPWSTR deformated = NULL;
2391 int count;
2393 deformat_string(package, &value[2], &deformated);
2395 /* binary value type */
2396 ptr = deformated;
2397 *type = REG_BINARY;
2398 if (strlenW(ptr)%2)
2399 *size = (strlenW(ptr)/2)+1;
2400 else
2401 *size = strlenW(ptr)/2;
2403 data = msi_alloc(*size);
2405 byte[0] = '0';
2406 byte[1] = 'x';
2407 byte[4] = 0;
2408 count = 0;
2409 /* if uneven pad with a zero in front */
2410 if (strlenW(ptr)%2)
2412 byte[2]= '0';
2413 byte[3]= *ptr;
2414 ptr++;
2415 data[count] = (BYTE)strtol(byte,NULL,0);
2416 count ++;
2417 TRACE("Uneven byte count\n");
2419 while (*ptr)
2421 byte[2]= *ptr;
2422 ptr++;
2423 byte[3]= *ptr;
2424 ptr++;
2425 data[count] = (BYTE)strtol(byte,NULL,0);
2426 count ++;
2428 msi_free(deformated);
2430 TRACE("Data %i bytes(%i)\n",*size,count);
2432 else
2434 LPWSTR deformated;
2435 LPWSTR p;
2436 DWORD d = 0;
2437 deformat_string(package, &value[1], &deformated);
2439 *type=REG_DWORD;
2440 *size = sizeof(DWORD);
2441 data = msi_alloc(*size);
2442 p = deformated;
2443 if (*p == '-')
2444 p++;
2445 while (*p)
2447 if ( (*p < '0') || (*p > '9') )
2448 break;
2449 d *= 10;
2450 d += (*p - '0');
2451 p++;
2453 if (deformated[0] == '-')
2454 d = -d;
2455 *(LPDWORD)data = d;
2456 TRACE("DWORD %i\n",*(LPDWORD)data);
2458 msi_free(deformated);
2461 else
2463 static const WCHAR szMulti[] = {'[','~',']',0};
2464 LPCWSTR ptr;
2465 *type=REG_SZ;
2467 if (value[0]=='#')
2469 if (value[1]=='%')
2471 ptr = &value[2];
2472 *type=REG_EXPAND_SZ;
2474 else
2475 ptr = &value[1];
2477 else
2478 ptr=value;
2480 if (strstrW(value, szMulti))
2481 *type = REG_MULTI_SZ;
2483 /* remove initial delimiter */
2484 if (!strncmpW(value, szMulti, 3))
2485 ptr = value + 3;
2487 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2489 /* add double NULL terminator */
2490 if (*type == REG_MULTI_SZ)
2492 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2493 data = msi_realloc_zero(data, *size);
2496 return data;
2499 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2501 const WCHAR *ret;
2503 switch (root)
2505 case -1:
2506 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2508 *root_key = HKEY_LOCAL_MACHINE;
2509 ret = szHLM;
2511 else
2513 *root_key = HKEY_CURRENT_USER;
2514 ret = szHCU;
2516 break;
2517 case 0:
2518 *root_key = HKEY_CLASSES_ROOT;
2519 ret = szHCR;
2520 break;
2521 case 1:
2522 *root_key = HKEY_CURRENT_USER;
2523 ret = szHCU;
2524 break;
2525 case 2:
2526 *root_key = HKEY_LOCAL_MACHINE;
2527 ret = szHLM;
2528 break;
2529 case 3:
2530 *root_key = HKEY_USERS;
2531 ret = szHU;
2532 break;
2533 default:
2534 ERR("Unknown root %i\n", root);
2535 return NULL;
2538 return ret;
2541 static WCHAR *get_keypath( MSIPACKAGE *package, HKEY root, const WCHAR *path )
2543 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2544 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2546 if (is_64bit && package->platform == PLATFORM_INTEL &&
2547 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2549 UINT size;
2550 WCHAR *path_32node;
2552 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2553 if (!(path_32node = msi_alloc( size ))) return NULL;
2555 memcpy( path_32node, path, len * sizeof(WCHAR) );
2556 strcpyW( path_32node + len, szWow6432Node );
2557 strcatW( path_32node, szBackSlash );
2558 strcatW( path_32node, path + len );
2559 return path_32node;
2562 return strdupW( path );
2565 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2567 MSIPACKAGE *package = param;
2568 LPSTR value_data = NULL;
2569 HKEY root_key, hkey;
2570 DWORD type,size;
2571 LPWSTR deformated, uikey, keypath;
2572 LPCWSTR szRoot, component, name, key, value;
2573 MSICOMPONENT *comp;
2574 MSIRECORD * uirow;
2575 INT root;
2576 BOOL check_first = FALSE;
2577 UINT rc;
2579 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2581 component = MSI_RecordGetString(row, 6);
2582 comp = msi_get_loaded_component(package,component);
2583 if (!comp)
2584 return ERROR_SUCCESS;
2586 comp->Action = msi_get_component_action( package, comp );
2587 if (comp->Action != INSTALLSTATE_LOCAL)
2589 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2590 return ERROR_SUCCESS;
2593 name = MSI_RecordGetString(row, 4);
2594 if( MSI_RecordIsNull(row,5) && name )
2596 /* null values can have special meanings */
2597 if (name[0]=='-' && name[1] == 0)
2598 return ERROR_SUCCESS;
2599 else if ((name[0]=='+' && name[1] == 0) ||
2600 (name[0] == '*' && name[1] == 0))
2601 name = NULL;
2602 check_first = TRUE;
2605 root = MSI_RecordGetInteger(row,2);
2606 key = MSI_RecordGetString(row, 3);
2608 szRoot = get_root_key( package, root, &root_key );
2609 if (!szRoot)
2610 return ERROR_SUCCESS;
2612 deformat_string(package, key , &deformated);
2613 size = strlenW(deformated) + strlenW(szRoot) + 1;
2614 uikey = msi_alloc(size*sizeof(WCHAR));
2615 strcpyW(uikey,szRoot);
2616 strcatW(uikey,deformated);
2618 keypath = get_keypath( package, root_key, deformated );
2619 msi_free( deformated );
2620 if (RegCreateKeyW( root_key, keypath, &hkey ))
2622 ERR("Could not create key %s\n", debugstr_w(keypath));
2623 msi_free(uikey);
2624 msi_free(keypath);
2625 return ERROR_SUCCESS;
2628 value = MSI_RecordGetString(row,5);
2629 if (value)
2630 value_data = parse_value(package, value, &type, &size);
2631 else
2633 value_data = (LPSTR)strdupW(szEmpty);
2634 size = sizeof(szEmpty);
2635 type = REG_SZ;
2638 deformat_string(package, name, &deformated);
2640 if (!check_first)
2642 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2643 debugstr_w(uikey));
2644 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2646 else
2648 DWORD sz = 0;
2649 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2650 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2652 TRACE("value %s of %s checked already exists\n",
2653 debugstr_w(deformated), debugstr_w(uikey));
2655 else
2657 TRACE("Checked and setting value %s of %s\n",
2658 debugstr_w(deformated), debugstr_w(uikey));
2659 if (deformated || size)
2660 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2663 RegCloseKey(hkey);
2665 uirow = MSI_CreateRecord(3);
2666 MSI_RecordSetStringW(uirow,2,deformated);
2667 MSI_RecordSetStringW(uirow,1,uikey);
2668 if (type == REG_SZ || type == REG_EXPAND_SZ)
2669 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2670 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2671 msiobj_release( &uirow->hdr );
2673 msi_free(value_data);
2674 msi_free(deformated);
2675 msi_free(uikey);
2676 msi_free(keypath);
2678 return ERROR_SUCCESS;
2681 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2683 static const WCHAR query[] = {
2684 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2685 '`','R','e','g','i','s','t','r','y','`',0};
2686 MSIQUERY *view;
2687 UINT rc;
2689 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2690 if (rc != ERROR_SUCCESS)
2691 return ERROR_SUCCESS;
2693 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2694 msiobj_release(&view->hdr);
2695 return rc;
2698 static void delete_reg_value( HKEY root, const WCHAR *keypath, const WCHAR *value )
2700 LONG res;
2701 HKEY hkey;
2702 DWORD num_subkeys, num_values;
2704 if (!(res = RegOpenKeyW( root, keypath, &hkey )))
2706 if ((res = RegDeleteValueW( hkey, value )))
2708 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2710 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2711 NULL, NULL, NULL, NULL );
2712 RegCloseKey( hkey );
2713 if (!res && !num_subkeys && !num_values)
2715 TRACE("removing empty key %s\n", debugstr_w(keypath));
2716 RegDeleteKeyW( root, keypath );
2718 return;
2720 TRACE("failed to open key %s (%d)\n", debugstr_w(keypath), res);
2723 static void delete_reg_key( HKEY root, const WCHAR *keypath )
2725 LONG res = RegDeleteTreeW( root, keypath );
2726 if (res) TRACE("failed to delete key %s (%d)\n", debugstr_w(keypath), res);
2729 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2731 MSIPACKAGE *package = param;
2732 LPCWSTR component, name, key_str, root_key_str;
2733 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2734 MSICOMPONENT *comp;
2735 MSIRECORD *uirow;
2736 BOOL delete_key = FALSE;
2737 HKEY hkey_root;
2738 UINT size;
2739 INT root;
2741 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2743 component = MSI_RecordGetString( row, 6 );
2744 comp = msi_get_loaded_component( package, component );
2745 if (!comp)
2746 return ERROR_SUCCESS;
2748 comp->Action = msi_get_component_action( package, comp );
2749 if (comp->Action != INSTALLSTATE_ABSENT)
2751 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2752 return ERROR_SUCCESS;
2755 name = MSI_RecordGetString( row, 4 );
2756 if (MSI_RecordIsNull( row, 5 ) && name )
2758 if (name[0] == '+' && !name[1])
2759 return ERROR_SUCCESS;
2760 else if ((name[0] == '-' && !name[1]) || (name[0] == '*' && !name[1]))
2762 delete_key = TRUE;
2763 name = NULL;
2767 root = MSI_RecordGetInteger( row, 2 );
2768 key_str = MSI_RecordGetString( row, 3 );
2770 root_key_str = get_root_key( package, root, &hkey_root );
2771 if (!root_key_str)
2772 return ERROR_SUCCESS;
2774 deformat_string( package, key_str, &deformated_key );
2775 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2776 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2777 strcpyW( ui_key_str, root_key_str );
2778 strcatW( ui_key_str, deformated_key );
2780 deformat_string( package, name, &deformated_name );
2782 keypath = get_keypath( package, hkey_root, deformated_key );
2783 msi_free( deformated_key );
2784 if (delete_key) delete_reg_key( hkey_root, keypath );
2785 else delete_reg_value( hkey_root, keypath, deformated_name );
2786 msi_free( keypath );
2788 uirow = MSI_CreateRecord( 2 );
2789 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2790 MSI_RecordSetStringW( uirow, 2, deformated_name );
2791 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2792 msiobj_release( &uirow->hdr );
2794 msi_free( ui_key_str );
2795 msi_free( deformated_name );
2796 return ERROR_SUCCESS;
2799 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2801 MSIPACKAGE *package = param;
2802 LPCWSTR component, name, key_str, root_key_str;
2803 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2804 MSICOMPONENT *comp;
2805 MSIRECORD *uirow;
2806 BOOL delete_key = FALSE;
2807 HKEY hkey_root;
2808 UINT size;
2809 INT root;
2811 component = MSI_RecordGetString( row, 5 );
2812 comp = msi_get_loaded_component( package, component );
2813 if (!comp)
2814 return ERROR_SUCCESS;
2816 comp->Action = msi_get_component_action( package, comp );
2817 if (comp->Action != INSTALLSTATE_LOCAL)
2819 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2820 return ERROR_SUCCESS;
2823 if ((name = MSI_RecordGetString( row, 4 )))
2825 if (name[0] == '-' && !name[1])
2827 delete_key = TRUE;
2828 name = NULL;
2832 root = MSI_RecordGetInteger( row, 2 );
2833 key_str = MSI_RecordGetString( row, 3 );
2835 root_key_str = get_root_key( package, root, &hkey_root );
2836 if (!root_key_str)
2837 return ERROR_SUCCESS;
2839 deformat_string( package, key_str, &deformated_key );
2840 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2841 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2842 strcpyW( ui_key_str, root_key_str );
2843 strcatW( ui_key_str, deformated_key );
2845 deformat_string( package, name, &deformated_name );
2847 keypath = get_keypath( package, hkey_root, deformated_key );
2848 msi_free( deformated_key );
2849 if (delete_key) delete_reg_key( hkey_root, keypath );
2850 else delete_reg_value( hkey_root, keypath, deformated_name );
2851 msi_free( keypath );
2853 uirow = MSI_CreateRecord( 2 );
2854 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2855 MSI_RecordSetStringW( uirow, 2, deformated_name );
2856 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2857 msiobj_release( &uirow->hdr );
2859 msi_free( ui_key_str );
2860 msi_free( deformated_name );
2861 return ERROR_SUCCESS;
2864 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2866 static const WCHAR registry_query[] = {
2867 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2868 '`','R','e','g','i','s','t','r','y','`',0};
2869 static const WCHAR remove_registry_query[] = {
2870 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2871 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2872 MSIQUERY *view;
2873 UINT rc;
2875 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2876 if (rc == ERROR_SUCCESS)
2878 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2879 msiobj_release( &view->hdr );
2880 if (rc != ERROR_SUCCESS)
2881 return rc;
2883 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2884 if (rc == ERROR_SUCCESS)
2886 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2887 msiobj_release( &view->hdr );
2888 if (rc != ERROR_SUCCESS)
2889 return rc;
2891 return ERROR_SUCCESS;
2894 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2896 package->script->CurrentlyScripting = TRUE;
2898 return ERROR_SUCCESS;
2902 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2904 static const WCHAR query[]= {
2905 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2906 '`','R','e','g','i','s','t','r','y','`',0};
2907 MSICOMPONENT *comp;
2908 DWORD total = 0, count = 0;
2909 MSIQUERY *view;
2910 MSIFEATURE *feature;
2911 MSIFILE *file;
2912 UINT rc;
2914 TRACE("InstallValidate\n");
2916 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2917 if (rc == ERROR_SUCCESS)
2919 rc = MSI_IterateRecords( view, &count, NULL, package );
2920 msiobj_release( &view->hdr );
2921 if (rc != ERROR_SUCCESS)
2922 return rc;
2923 total += count * REG_PROGRESS_VALUE;
2925 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2926 total += COMPONENT_PROGRESS_VALUE;
2928 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2929 total += file->FileSize;
2931 msi_ui_progress( package, 0, total, 0, 0 );
2933 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2935 TRACE("Feature: %s Installed %d Request %d Action %d\n",
2936 debugstr_w(feature->Feature), feature->Installed,
2937 feature->ActionRequest, feature->Action);
2939 return ERROR_SUCCESS;
2942 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2944 MSIPACKAGE* package = param;
2945 LPCWSTR cond = NULL;
2946 LPCWSTR message = NULL;
2947 UINT r;
2949 static const WCHAR title[]=
2950 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2952 cond = MSI_RecordGetString(row,1);
2954 r = MSI_EvaluateConditionW(package,cond);
2955 if (r == MSICONDITION_FALSE)
2957 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2959 LPWSTR deformated;
2960 message = MSI_RecordGetString(row,2);
2961 deformat_string(package,message,&deformated);
2962 MessageBoxW(NULL,deformated,title,MB_OK);
2963 msi_free(deformated);
2966 return ERROR_INSTALL_FAILURE;
2969 return ERROR_SUCCESS;
2972 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2974 static const WCHAR query[] = {
2975 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2976 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2977 MSIQUERY *view;
2978 UINT rc;
2980 TRACE("Checking launch conditions\n");
2982 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2983 if (rc != ERROR_SUCCESS)
2984 return ERROR_SUCCESS;
2986 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2987 msiobj_release(&view->hdr);
2988 return rc;
2991 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2994 if (!cmp->KeyPath)
2995 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
2997 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2999 static const WCHAR query[] = {
3000 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3001 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3002 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3003 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3004 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3005 MSIRECORD *row;
3006 UINT root, len;
3007 LPWSTR deformated, buffer, deformated_name;
3008 LPCWSTR key, name;
3010 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3011 if (!row)
3012 return NULL;
3014 root = MSI_RecordGetInteger(row,2);
3015 key = MSI_RecordGetString(row, 3);
3016 name = MSI_RecordGetString(row, 4);
3017 deformat_string(package, key , &deformated);
3018 deformat_string(package, name, &deformated_name);
3020 len = strlenW(deformated) + 6;
3021 if (deformated_name)
3022 len+=strlenW(deformated_name);
3024 buffer = msi_alloc( len *sizeof(WCHAR));
3026 if (deformated_name)
3027 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3028 else
3029 sprintfW(buffer,fmt,root,deformated);
3031 msi_free(deformated);
3032 msi_free(deformated_name);
3033 msiobj_release(&row->hdr);
3035 return buffer;
3037 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3039 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3040 return NULL;
3042 else
3044 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3046 if (file)
3047 return strdupW( file->TargetPath );
3049 return NULL;
3052 static HKEY openSharedDLLsKey(void)
3054 HKEY hkey=0;
3055 static const WCHAR path[] =
3056 {'S','o','f','t','w','a','r','e','\\',
3057 'M','i','c','r','o','s','o','f','t','\\',
3058 'W','i','n','d','o','w','s','\\',
3059 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3060 'S','h','a','r','e','d','D','L','L','s',0};
3062 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3063 return hkey;
3066 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3068 HKEY hkey;
3069 DWORD count=0;
3070 DWORD type;
3071 DWORD sz = sizeof(count);
3072 DWORD rc;
3074 hkey = openSharedDLLsKey();
3075 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3076 if (rc != ERROR_SUCCESS)
3077 count = 0;
3078 RegCloseKey(hkey);
3079 return count;
3082 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3084 HKEY hkey;
3086 hkey = openSharedDLLsKey();
3087 if (count > 0)
3088 msi_reg_set_val_dword( hkey, path, count );
3089 else
3090 RegDeleteValueW(hkey,path);
3091 RegCloseKey(hkey);
3092 return count;
3095 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3097 MSIFEATURE *feature;
3098 INT count = 0;
3099 BOOL write = FALSE;
3101 /* only refcount DLLs */
3102 if (comp->KeyPath == NULL ||
3103 comp->assembly ||
3104 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3105 comp->Attributes & msidbComponentAttributesODBCDataSource)
3106 write = FALSE;
3107 else
3109 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3110 write = (count > 0);
3112 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3113 write = TRUE;
3116 /* increment counts */
3117 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3119 ComponentList *cl;
3121 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3122 continue;
3124 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3126 if ( cl->component == comp )
3127 count++;
3131 /* decrement counts */
3132 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3134 ComponentList *cl;
3136 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3137 continue;
3139 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3141 if ( cl->component == comp )
3142 count--;
3146 /* ref count all the files in the component */
3147 if (write)
3149 MSIFILE *file;
3151 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3153 if (file->Component == comp)
3154 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3158 /* add a count for permanent */
3159 if (comp->Attributes & msidbComponentAttributesPermanent)
3160 count ++;
3162 comp->RefCount = count;
3164 if (write)
3165 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3168 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3170 if (comp->assembly)
3172 const WCHAR prefixW[] = {'<','\\',0};
3173 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3174 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3176 if (keypath)
3178 strcpyW( keypath, prefixW );
3179 strcatW( keypath, comp->assembly->display_name );
3181 return keypath;
3183 return resolve_keypath( package, comp );
3186 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3188 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3189 UINT rc;
3190 MSICOMPONENT *comp;
3191 HKEY hkey;
3193 TRACE("\n");
3195 squash_guid(package->ProductCode,squished_pc);
3196 msi_set_sourcedir_props(package, FALSE);
3198 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3200 MSIRECORD *uirow;
3201 INSTALLSTATE action;
3203 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3204 if (!comp->ComponentId)
3205 continue;
3207 squash_guid( comp->ComponentId, squished_cc );
3208 msi_free( comp->FullKeypath );
3209 comp->FullKeypath = build_full_keypath( package, comp );
3211 ACTION_RefCountComponent( package, comp );
3213 if (package->need_rollback) action = comp->Installed;
3214 else action = comp->ActionRequest;
3216 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3217 debugstr_w(comp->Component), debugstr_w(squished_cc),
3218 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3220 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3222 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3223 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3224 else
3225 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3227 if (rc != ERROR_SUCCESS)
3228 continue;
3230 if (comp->Attributes & msidbComponentAttributesPermanent)
3232 static const WCHAR szPermKey[] =
3233 { '0','0','0','0','0','0','0','0','0','0','0','0',
3234 '0','0','0','0','0','0','0','0','0','0','0','0',
3235 '0','0','0','0','0','0','0','0',0 };
3237 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3239 if (action == INSTALLSTATE_LOCAL)
3240 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3241 else
3243 MSIFILE *file;
3244 MSIRECORD *row;
3245 LPWSTR ptr, ptr2;
3246 WCHAR source[MAX_PATH];
3247 WCHAR base[MAX_PATH];
3248 LPWSTR sourcepath;
3250 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3251 static const WCHAR query[] = {
3252 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3253 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3254 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3255 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3256 '`','D','i','s','k','I','d','`',0};
3258 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3259 continue;
3261 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3262 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3263 ptr2 = strrchrW(source, '\\') + 1;
3264 msiobj_release(&row->hdr);
3266 lstrcpyW(base, package->PackagePath);
3267 ptr = strrchrW(base, '\\');
3268 *(ptr + 1) = '\0';
3270 sourcepath = msi_resolve_file_source(package, file);
3271 ptr = sourcepath + lstrlenW(base);
3272 lstrcpyW(ptr2, ptr);
3273 msi_free(sourcepath);
3275 msi_reg_set_val_str(hkey, squished_pc, source);
3277 RegCloseKey(hkey);
3279 else if (action == INSTALLSTATE_ABSENT)
3281 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3282 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3283 else
3284 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3287 /* UI stuff */
3288 uirow = MSI_CreateRecord(3);
3289 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3290 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3291 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3292 msi_ui_actiondata( package, szProcessComponents, uirow );
3293 msiobj_release( &uirow->hdr );
3295 return ERROR_SUCCESS;
3298 typedef struct {
3299 CLSID clsid;
3300 LPWSTR source;
3302 LPWSTR path;
3303 ITypeLib *ptLib;
3304 } typelib_struct;
3306 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3307 LPWSTR lpszName, LONG_PTR lParam)
3309 TLIBATTR *attr;
3310 typelib_struct *tl_struct = (typelib_struct*) lParam;
3311 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3312 int sz;
3313 HRESULT res;
3315 if (!IS_INTRESOURCE(lpszName))
3317 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3318 return TRUE;
3321 sz = strlenW(tl_struct->source)+4;
3322 sz *= sizeof(WCHAR);
3324 if ((INT_PTR)lpszName == 1)
3325 tl_struct->path = strdupW(tl_struct->source);
3326 else
3328 tl_struct->path = msi_alloc(sz);
3329 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3332 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3333 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3334 if (FAILED(res))
3336 msi_free(tl_struct->path);
3337 tl_struct->path = NULL;
3339 return TRUE;
3342 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3343 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3345 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3346 return FALSE;
3349 msi_free(tl_struct->path);
3350 tl_struct->path = NULL;
3352 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3353 ITypeLib_Release(tl_struct->ptLib);
3355 return TRUE;
3358 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3360 MSIPACKAGE* package = param;
3361 LPCWSTR component;
3362 MSICOMPONENT *comp;
3363 MSIFILE *file;
3364 typelib_struct tl_struct;
3365 ITypeLib *tlib;
3366 HMODULE module;
3367 HRESULT hr;
3369 component = MSI_RecordGetString(row,3);
3370 comp = msi_get_loaded_component(package,component);
3371 if (!comp)
3372 return ERROR_SUCCESS;
3374 comp->Action = msi_get_component_action( package, comp );
3375 if (comp->Action != INSTALLSTATE_LOCAL)
3377 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3378 return ERROR_SUCCESS;
3381 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3383 TRACE("component has no key path\n");
3384 return ERROR_SUCCESS;
3386 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3388 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3389 if (module)
3391 LPCWSTR guid;
3392 guid = MSI_RecordGetString(row,1);
3393 CLSIDFromString( guid, &tl_struct.clsid);
3394 tl_struct.source = strdupW( file->TargetPath );
3395 tl_struct.path = NULL;
3397 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3398 (LONG_PTR)&tl_struct);
3400 if (tl_struct.path)
3402 LPCWSTR helpid, help_path = NULL;
3403 HRESULT res;
3405 helpid = MSI_RecordGetString(row,6);
3407 if (helpid) help_path = msi_get_target_folder( package, helpid );
3408 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3410 if (FAILED(res))
3411 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3412 else
3413 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3415 ITypeLib_Release(tl_struct.ptLib);
3416 msi_free(tl_struct.path);
3418 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3420 FreeLibrary(module);
3421 msi_free(tl_struct.source);
3423 else
3425 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3426 if (FAILED(hr))
3428 ERR("Failed to load type library: %08x\n", hr);
3429 return ERROR_INSTALL_FAILURE;
3432 ITypeLib_Release(tlib);
3435 return ERROR_SUCCESS;
3438 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3440 static const WCHAR query[] = {
3441 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3442 '`','T','y','p','e','L','i','b','`',0};
3443 MSIQUERY *view;
3444 UINT rc;
3446 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3447 if (rc != ERROR_SUCCESS)
3448 return ERROR_SUCCESS;
3450 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3451 msiobj_release(&view->hdr);
3452 return rc;
3455 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3457 MSIPACKAGE *package = param;
3458 LPCWSTR component, guid;
3459 MSICOMPONENT *comp;
3460 GUID libid;
3461 UINT version;
3462 LCID language;
3463 SYSKIND syskind;
3464 HRESULT hr;
3466 component = MSI_RecordGetString( row, 3 );
3467 comp = msi_get_loaded_component( package, component );
3468 if (!comp)
3469 return ERROR_SUCCESS;
3471 comp->Action = msi_get_component_action( package, comp );
3472 if (comp->Action != INSTALLSTATE_ABSENT)
3474 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3475 return ERROR_SUCCESS;
3477 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3479 guid = MSI_RecordGetString( row, 1 );
3480 CLSIDFromString( guid, &libid );
3481 version = MSI_RecordGetInteger( row, 4 );
3482 language = MSI_RecordGetInteger( row, 2 );
3484 #ifdef _WIN64
3485 syskind = SYS_WIN64;
3486 #else
3487 syskind = SYS_WIN32;
3488 #endif
3490 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3491 if (FAILED(hr))
3493 WARN("Failed to unregister typelib: %08x\n", hr);
3496 return ERROR_SUCCESS;
3499 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3501 static const WCHAR query[] = {
3502 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3503 '`','T','y','p','e','L','i','b','`',0};
3504 MSIQUERY *view;
3505 UINT rc;
3507 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3508 if (rc != ERROR_SUCCESS)
3509 return ERROR_SUCCESS;
3511 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3512 msiobj_release( &view->hdr );
3513 return rc;
3516 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3518 static const WCHAR szlnk[] = {'.','l','n','k',0};
3519 LPCWSTR directory, extension, link_folder;
3520 LPWSTR link_file, filename;
3522 directory = MSI_RecordGetString( row, 2 );
3523 link_folder = msi_get_target_folder( package, directory );
3524 if (!link_folder)
3526 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3527 return NULL;
3529 /* may be needed because of a bug somewhere else */
3530 msi_create_full_path( link_folder );
3532 filename = msi_dup_record_field( row, 3 );
3533 msi_reduce_to_long_filename( filename );
3535 extension = strchrW( filename, '.' );
3536 if (!extension || strcmpiW( extension, szlnk ))
3538 int len = strlenW( filename );
3539 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3540 memcpy( filename + len, szlnk, sizeof(szlnk) );
3542 link_file = msi_build_directory_name( 2, link_folder, filename );
3543 msi_free( filename );
3545 return link_file;
3548 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3550 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3551 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3552 WCHAR *folder, *dest, *path;
3554 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3555 folder = msi_dup_property( package->db, szWindowsFolder );
3556 else
3558 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3559 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3560 msi_free( appdata );
3562 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3563 msi_create_full_path( dest );
3564 path = msi_build_directory_name( 2, dest, icon_name );
3565 msi_free( folder );
3566 msi_free( dest );
3567 return path;
3570 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3572 MSIPACKAGE *package = param;
3573 LPWSTR link_file, deformated, path;
3574 LPCWSTR component, target;
3575 MSICOMPONENT *comp;
3576 IShellLinkW *sl = NULL;
3577 IPersistFile *pf = NULL;
3578 HRESULT res;
3580 component = MSI_RecordGetString(row, 4);
3581 comp = msi_get_loaded_component(package, component);
3582 if (!comp)
3583 return ERROR_SUCCESS;
3585 comp->Action = msi_get_component_action( package, comp );
3586 if (comp->Action != INSTALLSTATE_LOCAL)
3588 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3589 return ERROR_SUCCESS;
3591 msi_ui_actiondata( package, szCreateShortcuts, row );
3593 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3594 &IID_IShellLinkW, (LPVOID *) &sl );
3596 if (FAILED( res ))
3598 ERR("CLSID_ShellLink not available\n");
3599 goto err;
3602 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3603 if (FAILED( res ))
3605 ERR("QueryInterface(IID_IPersistFile) failed\n");
3606 goto err;
3609 target = MSI_RecordGetString(row, 5);
3610 if (strchrW(target, '['))
3612 deformat_string( package, target, &path );
3613 TRACE("target path is %s\n", debugstr_w(path));
3614 IShellLinkW_SetPath( sl, path );
3615 msi_free( path );
3617 else
3619 FIXME("poorly handled shortcut format, advertised shortcut\n");
3620 IShellLinkW_SetPath(sl,comp->FullKeypath);
3623 if (!MSI_RecordIsNull(row,6))
3625 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3626 deformat_string(package, arguments, &deformated);
3627 IShellLinkW_SetArguments(sl,deformated);
3628 msi_free(deformated);
3631 if (!MSI_RecordIsNull(row,7))
3633 LPCWSTR description = MSI_RecordGetString(row, 7);
3634 IShellLinkW_SetDescription(sl, description);
3637 if (!MSI_RecordIsNull(row,8))
3638 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3640 if (!MSI_RecordIsNull(row,9))
3642 INT index;
3643 LPCWSTR icon = MSI_RecordGetString(row, 9);
3645 path = msi_build_icon_path(package, icon);
3646 index = MSI_RecordGetInteger(row,10);
3648 /* no value means 0 */
3649 if (index == MSI_NULL_INTEGER)
3650 index = 0;
3652 IShellLinkW_SetIconLocation(sl, path, index);
3653 msi_free(path);
3656 if (!MSI_RecordIsNull(row,11))
3657 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3659 if (!MSI_RecordIsNull(row,12))
3661 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3662 full_path = msi_get_target_folder( package, wkdir );
3663 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3665 link_file = get_link_file(package, row);
3667 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3668 IPersistFile_Save(pf, link_file, FALSE);
3669 msi_free(link_file);
3671 err:
3672 if (pf)
3673 IPersistFile_Release( pf );
3674 if (sl)
3675 IShellLinkW_Release( sl );
3677 return ERROR_SUCCESS;
3680 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3682 static const WCHAR query[] = {
3683 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3684 '`','S','h','o','r','t','c','u','t','`',0};
3685 MSIQUERY *view;
3686 HRESULT res;
3687 UINT rc;
3689 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3690 if (rc != ERROR_SUCCESS)
3691 return ERROR_SUCCESS;
3693 res = CoInitialize( NULL );
3695 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3696 msiobj_release(&view->hdr);
3698 if (SUCCEEDED(res)) CoUninitialize();
3699 return rc;
3702 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3704 MSIPACKAGE *package = param;
3705 LPWSTR link_file;
3706 LPCWSTR component;
3707 MSICOMPONENT *comp;
3709 component = MSI_RecordGetString( row, 4 );
3710 comp = msi_get_loaded_component( package, component );
3711 if (!comp)
3712 return ERROR_SUCCESS;
3714 comp->Action = msi_get_component_action( package, comp );
3715 if (comp->Action != INSTALLSTATE_ABSENT)
3717 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3718 return ERROR_SUCCESS;
3720 msi_ui_actiondata( package, szRemoveShortcuts, row );
3722 link_file = get_link_file( package, row );
3724 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3725 if (!DeleteFileW( link_file ))
3727 WARN("Failed to remove shortcut file %u\n", GetLastError());
3729 msi_free( link_file );
3731 return ERROR_SUCCESS;
3734 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3736 static const WCHAR query[] = {
3737 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3738 '`','S','h','o','r','t','c','u','t','`',0};
3739 MSIQUERY *view;
3740 UINT rc;
3742 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3743 if (rc != ERROR_SUCCESS)
3744 return ERROR_SUCCESS;
3746 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3747 msiobj_release( &view->hdr );
3748 return rc;
3751 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3753 MSIPACKAGE* package = param;
3754 HANDLE the_file;
3755 LPWSTR FilePath;
3756 LPCWSTR FileName;
3757 CHAR buffer[1024];
3758 DWORD sz;
3759 UINT rc;
3761 FileName = MSI_RecordGetString(row,1);
3762 if (!FileName)
3764 ERR("Unable to get FileName\n");
3765 return ERROR_SUCCESS;
3768 FilePath = msi_build_icon_path(package, FileName);
3770 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3772 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3773 FILE_ATTRIBUTE_NORMAL, NULL);
3775 if (the_file == INVALID_HANDLE_VALUE)
3777 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3778 msi_free(FilePath);
3779 return ERROR_SUCCESS;
3784 DWORD write;
3785 sz = 1024;
3786 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3787 if (rc != ERROR_SUCCESS)
3789 ERR("Failed to get stream\n");
3790 CloseHandle(the_file);
3791 DeleteFileW(FilePath);
3792 break;
3794 WriteFile(the_file,buffer,sz,&write,NULL);
3795 } while (sz == 1024);
3797 msi_free(FilePath);
3798 CloseHandle(the_file);
3800 return ERROR_SUCCESS;
3803 static UINT msi_publish_icons(MSIPACKAGE *package)
3805 static const WCHAR query[]= {
3806 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3807 '`','I','c','o','n','`',0};
3808 MSIQUERY *view;
3809 UINT r;
3811 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3812 if (r == ERROR_SUCCESS)
3814 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3815 msiobj_release(&view->hdr);
3816 if (r != ERROR_SUCCESS)
3817 return r;
3819 return ERROR_SUCCESS;
3822 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3824 UINT r;
3825 HKEY source;
3826 LPWSTR buffer;
3827 MSIMEDIADISK *disk;
3828 MSISOURCELISTINFO *info;
3830 r = RegCreateKeyW(hkey, szSourceList, &source);
3831 if (r != ERROR_SUCCESS)
3832 return r;
3834 RegCloseKey(source);
3836 buffer = strrchrW(package->PackagePath, '\\') + 1;
3837 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3838 package->Context, MSICODE_PRODUCT,
3839 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3840 if (r != ERROR_SUCCESS)
3841 return r;
3843 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3844 package->Context, MSICODE_PRODUCT,
3845 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3846 if (r != ERROR_SUCCESS)
3847 return r;
3849 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3850 package->Context, MSICODE_PRODUCT,
3851 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3852 if (r != ERROR_SUCCESS)
3853 return r;
3855 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3857 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3858 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3859 info->options, info->value);
3860 else
3861 MsiSourceListSetInfoW(package->ProductCode, NULL,
3862 info->context, info->options,
3863 info->property, info->value);
3866 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3868 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3869 disk->context, disk->options,
3870 disk->disk_id, disk->volume_label, disk->disk_prompt);
3873 return ERROR_SUCCESS;
3876 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3878 MSIHANDLE hdb, suminfo;
3879 WCHAR guids[MAX_PATH];
3880 WCHAR packcode[SQUISH_GUID_SIZE];
3881 LPWSTR buffer;
3882 LPWSTR ptr;
3883 DWORD langid;
3884 DWORD size;
3885 UINT r;
3887 static const WCHAR szARPProductIcon[] =
3888 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3889 static const WCHAR szAssignment[] =
3890 {'A','s','s','i','g','n','m','e','n','t',0};
3891 static const WCHAR szAdvertiseFlags[] =
3892 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3893 static const WCHAR szClients[] =
3894 {'C','l','i','e','n','t','s',0};
3895 static const WCHAR szColon[] = {':',0};
3897 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3898 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3899 msi_free(buffer);
3901 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3902 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3904 /* FIXME */
3905 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3907 buffer = msi_dup_property(package->db, szARPProductIcon);
3908 if (buffer)
3910 LPWSTR path = msi_build_icon_path(package, buffer);
3911 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3912 msi_free(path);
3913 msi_free(buffer);
3916 buffer = msi_dup_property(package->db, szProductVersion);
3917 if (buffer)
3919 DWORD verdword = msi_version_str_to_dword(buffer);
3920 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3921 msi_free(buffer);
3924 msi_reg_set_val_dword(hkey, szAssignment, 0);
3925 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3926 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3927 msi_reg_set_val_str(hkey, szClients, szColon);
3929 hdb = alloc_msihandle(&package->db->hdr);
3930 if (!hdb)
3931 return ERROR_NOT_ENOUGH_MEMORY;
3933 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3934 MsiCloseHandle(hdb);
3935 if (r != ERROR_SUCCESS)
3936 goto done;
3938 size = MAX_PATH;
3939 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3940 NULL, guids, &size);
3941 if (r != ERROR_SUCCESS)
3942 goto done;
3944 ptr = strchrW(guids, ';');
3945 if (ptr) *ptr = 0;
3946 squash_guid(guids, packcode);
3947 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3949 done:
3950 MsiCloseHandle(suminfo);
3951 return ERROR_SUCCESS;
3954 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3956 UINT r;
3957 HKEY hkey;
3958 LPWSTR upgrade;
3959 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3961 upgrade = msi_dup_property(package->db, szUpgradeCode);
3962 if (!upgrade)
3963 return ERROR_SUCCESS;
3965 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3966 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3967 else
3968 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3970 if (r != ERROR_SUCCESS)
3972 WARN("failed to open upgrade code key\n");
3973 msi_free(upgrade);
3974 return ERROR_SUCCESS;
3976 squash_guid(package->ProductCode, squashed_pc);
3977 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3978 RegCloseKey(hkey);
3979 msi_free(upgrade);
3980 return ERROR_SUCCESS;
3983 static BOOL msi_check_publish(MSIPACKAGE *package)
3985 MSIFEATURE *feature;
3987 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3989 feature->Action = msi_get_feature_action( package, feature );
3990 if (feature->Action == INSTALLSTATE_LOCAL)
3991 return TRUE;
3994 return FALSE;
3997 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3999 MSIFEATURE *feature;
4001 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4003 feature->Action = msi_get_feature_action( package, feature );
4004 if (feature->Action != INSTALLSTATE_ABSENT)
4005 return FALSE;
4008 return TRUE;
4011 static UINT msi_publish_patches( MSIPACKAGE *package )
4013 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4014 WCHAR patch_squashed[GUID_SIZE];
4015 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4016 LONG res;
4017 MSIPATCHINFO *patch;
4018 UINT r;
4019 WCHAR *p, *all_patches = NULL;
4020 DWORD len = 0;
4022 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4023 if (r != ERROR_SUCCESS)
4024 return ERROR_FUNCTION_FAILED;
4026 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4027 if (res != ERROR_SUCCESS)
4029 r = ERROR_FUNCTION_FAILED;
4030 goto done;
4033 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4034 if (r != ERROR_SUCCESS)
4035 goto done;
4037 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4039 squash_guid( patch->patchcode, patch_squashed );
4040 len += strlenW( patch_squashed ) + 1;
4043 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4044 if (!all_patches)
4045 goto done;
4047 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4049 HKEY patch_key;
4051 squash_guid( patch->patchcode, p );
4052 p += strlenW( p ) + 1;
4054 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4055 (const BYTE *)patch->transforms,
4056 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4057 if (res != ERROR_SUCCESS)
4058 goto done;
4060 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4061 if (r != ERROR_SUCCESS)
4062 goto done;
4064 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4065 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4066 RegCloseKey( patch_key );
4067 if (res != ERROR_SUCCESS)
4068 goto done;
4070 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4072 res = GetLastError();
4073 ERR("Unable to copy patch package %d\n", res);
4074 goto done;
4076 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4077 if (res != ERROR_SUCCESS)
4078 goto done;
4080 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4081 RegCloseKey( patch_key );
4082 if (res != ERROR_SUCCESS)
4083 goto done;
4086 all_patches[len] = 0;
4087 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4088 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4089 if (res != ERROR_SUCCESS)
4090 goto done;
4092 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4093 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4094 if (res != ERROR_SUCCESS)
4095 r = ERROR_FUNCTION_FAILED;
4097 done:
4098 RegCloseKey( product_patches_key );
4099 RegCloseKey( patches_key );
4100 RegCloseKey( product_key );
4101 msi_free( all_patches );
4102 return r;
4105 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4107 UINT rc;
4108 HKEY hukey = NULL, hudkey = NULL;
4109 MSIRECORD *uirow;
4111 if (!list_empty(&package->patches))
4113 rc = msi_publish_patches(package);
4114 if (rc != ERROR_SUCCESS)
4115 goto end;
4118 /* FIXME: also need to publish if the product is in advertise mode */
4119 if (!msi_check_publish(package))
4120 return ERROR_SUCCESS;
4122 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4123 &hukey, TRUE);
4124 if (rc != ERROR_SUCCESS)
4125 goto end;
4127 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4128 NULL, &hudkey, TRUE);
4129 if (rc != ERROR_SUCCESS)
4130 goto end;
4132 rc = msi_publish_upgrade_code(package);
4133 if (rc != ERROR_SUCCESS)
4134 goto end;
4136 rc = msi_publish_product_properties(package, hukey);
4137 if (rc != ERROR_SUCCESS)
4138 goto end;
4140 rc = msi_publish_sourcelist(package, hukey);
4141 if (rc != ERROR_SUCCESS)
4142 goto end;
4144 rc = msi_publish_icons(package);
4146 end:
4147 uirow = MSI_CreateRecord( 1 );
4148 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4149 msi_ui_actiondata( package, szPublishProduct, uirow );
4150 msiobj_release( &uirow->hdr );
4152 RegCloseKey(hukey);
4153 RegCloseKey(hudkey);
4154 return rc;
4157 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4159 WCHAR *filename, *ptr, *folder, *ret;
4160 const WCHAR *dirprop;
4162 filename = msi_dup_record_field( row, 2 );
4163 if (filename && (ptr = strchrW( filename, '|' )))
4164 ptr++;
4165 else
4166 ptr = filename;
4168 dirprop = MSI_RecordGetString( row, 3 );
4169 if (dirprop)
4171 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4172 if (!folder) folder = msi_dup_property( package->db, dirprop );
4174 else
4175 folder = msi_dup_property( package->db, szWindowsFolder );
4177 if (!folder)
4179 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4180 msi_free( filename );
4181 return NULL;
4184 ret = msi_build_directory_name( 2, folder, ptr );
4186 msi_free( filename );
4187 msi_free( folder );
4188 return ret;
4191 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4193 MSIPACKAGE *package = param;
4194 LPCWSTR component, section, key, value, identifier;
4195 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4196 MSIRECORD * uirow;
4197 INT action;
4198 MSICOMPONENT *comp;
4200 component = MSI_RecordGetString(row, 8);
4201 comp = msi_get_loaded_component(package,component);
4202 if (!comp)
4203 return ERROR_SUCCESS;
4205 comp->Action = msi_get_component_action( package, comp );
4206 if (comp->Action != INSTALLSTATE_LOCAL)
4208 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4209 return ERROR_SUCCESS;
4212 identifier = MSI_RecordGetString(row,1);
4213 section = MSI_RecordGetString(row,4);
4214 key = MSI_RecordGetString(row,5);
4215 value = MSI_RecordGetString(row,6);
4216 action = MSI_RecordGetInteger(row,7);
4218 deformat_string(package,section,&deformated_section);
4219 deformat_string(package,key,&deformated_key);
4220 deformat_string(package,value,&deformated_value);
4222 fullname = get_ini_file_name(package, row);
4224 if (action == 0)
4226 TRACE("Adding value %s to section %s in %s\n",
4227 debugstr_w(deformated_key), debugstr_w(deformated_section),
4228 debugstr_w(fullname));
4229 WritePrivateProfileStringW(deformated_section, deformated_key,
4230 deformated_value, fullname);
4232 else if (action == 1)
4234 WCHAR returned[10];
4235 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4236 returned, 10, fullname);
4237 if (returned[0] == 0)
4239 TRACE("Adding value %s to section %s in %s\n",
4240 debugstr_w(deformated_key), debugstr_w(deformated_section),
4241 debugstr_w(fullname));
4243 WritePrivateProfileStringW(deformated_section, deformated_key,
4244 deformated_value, fullname);
4247 else if (action == 3)
4248 FIXME("Append to existing section not yet implemented\n");
4250 uirow = MSI_CreateRecord(4);
4251 MSI_RecordSetStringW(uirow,1,identifier);
4252 MSI_RecordSetStringW(uirow,2,deformated_section);
4253 MSI_RecordSetStringW(uirow,3,deformated_key);
4254 MSI_RecordSetStringW(uirow,4,deformated_value);
4255 msi_ui_actiondata( package, szWriteIniValues, uirow );
4256 msiobj_release( &uirow->hdr );
4258 msi_free(fullname);
4259 msi_free(deformated_key);
4260 msi_free(deformated_value);
4261 msi_free(deformated_section);
4262 return ERROR_SUCCESS;
4265 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4267 static const WCHAR query[] = {
4268 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4269 '`','I','n','i','F','i','l','e','`',0};
4270 MSIQUERY *view;
4271 UINT rc;
4273 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4274 if (rc != ERROR_SUCCESS)
4275 return ERROR_SUCCESS;
4277 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4278 msiobj_release(&view->hdr);
4279 return rc;
4282 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4284 MSIPACKAGE *package = param;
4285 LPCWSTR component, section, key, value, identifier;
4286 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4287 MSICOMPONENT *comp;
4288 MSIRECORD *uirow;
4289 INT action;
4291 component = MSI_RecordGetString( row, 8 );
4292 comp = msi_get_loaded_component( package, component );
4293 if (!comp)
4294 return ERROR_SUCCESS;
4296 comp->Action = msi_get_component_action( package, comp );
4297 if (comp->Action != INSTALLSTATE_ABSENT)
4299 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4300 return ERROR_SUCCESS;
4303 identifier = MSI_RecordGetString( row, 1 );
4304 section = MSI_RecordGetString( row, 4 );
4305 key = MSI_RecordGetString( row, 5 );
4306 value = MSI_RecordGetString( row, 6 );
4307 action = MSI_RecordGetInteger( row, 7 );
4309 deformat_string( package, section, &deformated_section );
4310 deformat_string( package, key, &deformated_key );
4311 deformat_string( package, value, &deformated_value );
4313 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4315 filename = get_ini_file_name( package, row );
4317 TRACE("Removing key %s from section %s in %s\n",
4318 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4320 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4322 WARN("Unable to remove key %u\n", GetLastError());
4324 msi_free( filename );
4326 else
4327 FIXME("Unsupported action %d\n", action);
4330 uirow = MSI_CreateRecord( 4 );
4331 MSI_RecordSetStringW( uirow, 1, identifier );
4332 MSI_RecordSetStringW( uirow, 2, deformated_section );
4333 MSI_RecordSetStringW( uirow, 3, deformated_key );
4334 MSI_RecordSetStringW( uirow, 4, deformated_value );
4335 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4336 msiobj_release( &uirow->hdr );
4338 msi_free( deformated_key );
4339 msi_free( deformated_value );
4340 msi_free( deformated_section );
4341 return ERROR_SUCCESS;
4344 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4346 MSIPACKAGE *package = param;
4347 LPCWSTR component, section, key, value, identifier;
4348 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4349 MSICOMPONENT *comp;
4350 MSIRECORD *uirow;
4351 INT action;
4353 component = MSI_RecordGetString( row, 8 );
4354 comp = msi_get_loaded_component( package, component );
4355 if (!comp)
4356 return ERROR_SUCCESS;
4358 comp->Action = msi_get_component_action( package, comp );
4359 if (comp->Action != INSTALLSTATE_LOCAL)
4361 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4362 return ERROR_SUCCESS;
4365 identifier = MSI_RecordGetString( row, 1 );
4366 section = MSI_RecordGetString( row, 4 );
4367 key = MSI_RecordGetString( row, 5 );
4368 value = MSI_RecordGetString( row, 6 );
4369 action = MSI_RecordGetInteger( row, 7 );
4371 deformat_string( package, section, &deformated_section );
4372 deformat_string( package, key, &deformated_key );
4373 deformat_string( package, value, &deformated_value );
4375 if (action == msidbIniFileActionRemoveLine)
4377 filename = get_ini_file_name( package, row );
4379 TRACE("Removing key %s from section %s in %s\n",
4380 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4382 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4384 WARN("Unable to remove key %u\n", GetLastError());
4386 msi_free( filename );
4388 else
4389 FIXME("Unsupported action %d\n", action);
4391 uirow = MSI_CreateRecord( 4 );
4392 MSI_RecordSetStringW( uirow, 1, identifier );
4393 MSI_RecordSetStringW( uirow, 2, deformated_section );
4394 MSI_RecordSetStringW( uirow, 3, deformated_key );
4395 MSI_RecordSetStringW( uirow, 4, deformated_value );
4396 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4397 msiobj_release( &uirow->hdr );
4399 msi_free( deformated_key );
4400 msi_free( deformated_value );
4401 msi_free( deformated_section );
4402 return ERROR_SUCCESS;
4405 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4407 static const WCHAR query[] = {
4408 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4409 '`','I','n','i','F','i','l','e','`',0};
4410 static const WCHAR remove_query[] = {
4411 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4412 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4413 MSIQUERY *view;
4414 UINT rc;
4416 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4417 if (rc == ERROR_SUCCESS)
4419 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4420 msiobj_release( &view->hdr );
4421 if (rc != ERROR_SUCCESS)
4422 return rc;
4424 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4425 if (rc == ERROR_SUCCESS)
4427 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4428 msiobj_release( &view->hdr );
4429 if (rc != ERROR_SUCCESS)
4430 return rc;
4432 return ERROR_SUCCESS;
4435 static void register_dll( const WCHAR *dll, BOOL unregister )
4437 HMODULE hmod;
4439 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4440 if (hmod)
4442 HRESULT (WINAPI *func_ptr)( void );
4443 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4445 func_ptr = (void *)GetProcAddress( hmod, func );
4446 if (func_ptr)
4448 HRESULT hr = func_ptr();
4449 if (FAILED( hr ))
4450 WARN("failed to register dll 0x%08x\n", hr);
4452 else
4453 WARN("entry point %s not found\n", func);
4454 FreeLibrary( hmod );
4455 return;
4457 WARN("failed to load library %u\n", GetLastError());
4460 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4462 MSIPACKAGE *package = param;
4463 LPCWSTR filename;
4464 MSIFILE *file;
4465 MSIRECORD *uirow;
4467 filename = MSI_RecordGetString( row, 1 );
4468 file = msi_get_loaded_file( package, filename );
4469 if (!file)
4471 WARN("unable to find file %s\n", debugstr_w(filename));
4472 return ERROR_SUCCESS;
4474 file->Component->Action = msi_get_component_action( package, file->Component );
4475 if (file->Component->Action != INSTALLSTATE_LOCAL)
4477 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4478 return ERROR_SUCCESS;
4481 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4482 register_dll( file->TargetPath, FALSE );
4484 uirow = MSI_CreateRecord( 2 );
4485 MSI_RecordSetStringW( uirow, 1, file->File );
4486 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4487 msi_ui_actiondata( package, szSelfRegModules, uirow );
4488 msiobj_release( &uirow->hdr );
4490 return ERROR_SUCCESS;
4493 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4495 static const WCHAR query[] = {
4496 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4497 '`','S','e','l','f','R','e','g','`',0};
4498 MSIQUERY *view;
4499 UINT rc;
4501 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4502 if (rc != ERROR_SUCCESS)
4503 return ERROR_SUCCESS;
4505 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4506 msiobj_release(&view->hdr);
4507 return rc;
4510 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4512 MSIPACKAGE *package = param;
4513 LPCWSTR filename;
4514 MSIFILE *file;
4515 MSIRECORD *uirow;
4517 filename = MSI_RecordGetString( row, 1 );
4518 file = msi_get_loaded_file( package, filename );
4519 if (!file)
4521 WARN("unable to find file %s\n", debugstr_w(filename));
4522 return ERROR_SUCCESS;
4524 file->Component->Action = msi_get_component_action( package, file->Component );
4525 if (file->Component->Action != INSTALLSTATE_ABSENT)
4527 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4528 return ERROR_SUCCESS;
4531 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4532 register_dll( file->TargetPath, TRUE );
4534 uirow = MSI_CreateRecord( 2 );
4535 MSI_RecordSetStringW( uirow, 1, file->File );
4536 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4537 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4538 msiobj_release( &uirow->hdr );
4540 return ERROR_SUCCESS;
4543 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4545 static const WCHAR query[] = {
4546 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4547 '`','S','e','l','f','R','e','g','`',0};
4548 MSIQUERY *view;
4549 UINT rc;
4551 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4552 if (rc != ERROR_SUCCESS)
4553 return ERROR_SUCCESS;
4555 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4556 msiobj_release( &view->hdr );
4557 return rc;
4560 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4562 MSIFEATURE *feature;
4563 UINT rc;
4564 HKEY hkey = NULL, userdata = NULL;
4566 if (!msi_check_publish(package))
4567 return ERROR_SUCCESS;
4569 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4570 &hkey, TRUE);
4571 if (rc != ERROR_SUCCESS)
4572 goto end;
4574 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4575 &userdata, TRUE);
4576 if (rc != ERROR_SUCCESS)
4577 goto end;
4579 /* here the guids are base 85 encoded */
4580 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4582 ComponentList *cl;
4583 LPWSTR data = NULL;
4584 GUID clsid;
4585 INT size;
4586 BOOL absent = FALSE;
4587 MSIRECORD *uirow;
4589 if (feature->Action != INSTALLSTATE_LOCAL &&
4590 feature->Action != INSTALLSTATE_SOURCE &&
4591 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4593 size = 1;
4594 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4596 size += 21;
4598 if (feature->Feature_Parent)
4599 size += strlenW( feature->Feature_Parent )+2;
4601 data = msi_alloc(size * sizeof(WCHAR));
4603 data[0] = 0;
4604 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4606 MSICOMPONENT* component = cl->component;
4607 WCHAR buf[21];
4609 buf[0] = 0;
4610 if (component->ComponentId)
4612 TRACE("From %s\n",debugstr_w(component->ComponentId));
4613 CLSIDFromString(component->ComponentId, &clsid);
4614 encode_base85_guid(&clsid,buf);
4615 TRACE("to %s\n",debugstr_w(buf));
4616 strcatW(data,buf);
4620 if (feature->Feature_Parent)
4622 static const WCHAR sep[] = {'\2',0};
4623 strcatW(data,sep);
4624 strcatW(data,feature->Feature_Parent);
4627 msi_reg_set_val_str( userdata, feature->Feature, data );
4628 msi_free(data);
4630 size = 0;
4631 if (feature->Feature_Parent)
4632 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4633 if (!absent)
4635 size += sizeof(WCHAR);
4636 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4637 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4639 else
4641 size += 2*sizeof(WCHAR);
4642 data = msi_alloc(size);
4643 data[0] = 0x6;
4644 data[1] = 0;
4645 if (feature->Feature_Parent)
4646 strcpyW( &data[1], feature->Feature_Parent );
4647 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4648 (LPBYTE)data,size);
4649 msi_free(data);
4652 /* the UI chunk */
4653 uirow = MSI_CreateRecord( 1 );
4654 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4655 msi_ui_actiondata( package, szPublishFeatures, uirow );
4656 msiobj_release( &uirow->hdr );
4657 /* FIXME: call msi_ui_progress? */
4660 end:
4661 RegCloseKey(hkey);
4662 RegCloseKey(userdata);
4663 return rc;
4666 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4668 UINT r;
4669 HKEY hkey;
4670 MSIRECORD *uirow;
4672 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4674 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4675 &hkey, FALSE);
4676 if (r == ERROR_SUCCESS)
4678 RegDeleteValueW(hkey, feature->Feature);
4679 RegCloseKey(hkey);
4682 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4683 &hkey, FALSE);
4684 if (r == ERROR_SUCCESS)
4686 RegDeleteValueW(hkey, feature->Feature);
4687 RegCloseKey(hkey);
4690 uirow = MSI_CreateRecord( 1 );
4691 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4692 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4693 msiobj_release( &uirow->hdr );
4695 return ERROR_SUCCESS;
4698 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4700 MSIFEATURE *feature;
4702 if (!msi_check_unpublish(package))
4703 return ERROR_SUCCESS;
4705 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4707 msi_unpublish_feature(package, feature);
4710 return ERROR_SUCCESS;
4713 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4715 SYSTEMTIME systime;
4716 DWORD size, langid;
4717 WCHAR date[9], *val, *buffer;
4718 const WCHAR *prop, *key;
4720 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4721 static const WCHAR modpath_fmt[] =
4722 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4723 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4724 static const WCHAR szModifyPath[] =
4725 {'M','o','d','i','f','y','P','a','t','h',0};
4726 static const WCHAR szUninstallString[] =
4727 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4728 static const WCHAR szEstimatedSize[] =
4729 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4730 static const WCHAR szDisplayVersion[] =
4731 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4732 static const WCHAR szInstallSource[] =
4733 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4734 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4735 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4736 static const WCHAR szAuthorizedCDFPrefix[] =
4737 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4738 static const WCHAR szARPCONTACT[] =
4739 {'A','R','P','C','O','N','T','A','C','T',0};
4740 static const WCHAR szContact[] =
4741 {'C','o','n','t','a','c','t',0};
4742 static const WCHAR szARPCOMMENTS[] =
4743 {'A','R','P','C','O','M','M','E','N','T','S',0};
4744 static const WCHAR szComments[] =
4745 {'C','o','m','m','e','n','t','s',0};
4746 static const WCHAR szProductName[] =
4747 {'P','r','o','d','u','c','t','N','a','m','e',0};
4748 static const WCHAR szDisplayName[] =
4749 {'D','i','s','p','l','a','y','N','a','m','e',0};
4750 static const WCHAR szARPHELPLINK[] =
4751 {'A','R','P','H','E','L','P','L','I','N','K',0};
4752 static const WCHAR szHelpLink[] =
4753 {'H','e','l','p','L','i','n','k',0};
4754 static const WCHAR szARPHELPTELEPHONE[] =
4755 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4756 static const WCHAR szHelpTelephone[] =
4757 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4758 static const WCHAR szARPINSTALLLOCATION[] =
4759 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4760 static const WCHAR szInstallLocation[] =
4761 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4762 static const WCHAR szManufacturer[] =
4763 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4764 static const WCHAR szPublisher[] =
4765 {'P','u','b','l','i','s','h','e','r',0};
4766 static const WCHAR szARPREADME[] =
4767 {'A','R','P','R','E','A','D','M','E',0};
4768 static const WCHAR szReadme[] =
4769 {'R','e','a','d','M','e',0};
4770 static const WCHAR szARPSIZE[] =
4771 {'A','R','P','S','I','Z','E',0};
4772 static const WCHAR szSize[] =
4773 {'S','i','z','e',0};
4774 static const WCHAR szARPURLINFOABOUT[] =
4775 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4776 static const WCHAR szURLInfoAbout[] =
4777 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4778 static const WCHAR szARPURLUPDATEINFO[] =
4779 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4780 static const WCHAR szURLUpdateInfo[] =
4781 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4782 static const WCHAR szARPSYSTEMCOMPONENT[] =
4783 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4784 static const WCHAR szSystemComponent[] =
4785 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4787 static const WCHAR *propval[] = {
4788 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4789 szARPCONTACT, szContact,
4790 szARPCOMMENTS, szComments,
4791 szProductName, szDisplayName,
4792 szARPHELPLINK, szHelpLink,
4793 szARPHELPTELEPHONE, szHelpTelephone,
4794 szARPINSTALLLOCATION, szInstallLocation,
4795 szSourceDir, szInstallSource,
4796 szManufacturer, szPublisher,
4797 szARPREADME, szReadme,
4798 szARPSIZE, szSize,
4799 szARPURLINFOABOUT, szURLInfoAbout,
4800 szARPURLUPDATEINFO, szURLUpdateInfo,
4801 NULL
4803 const WCHAR **p = propval;
4805 while (*p)
4807 prop = *p++;
4808 key = *p++;
4809 val = msi_dup_property(package->db, prop);
4810 msi_reg_set_val_str(hkey, key, val);
4811 msi_free(val);
4814 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4815 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4817 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4819 size = deformat_string(package, modpath_fmt, &buffer);
4820 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4821 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4822 msi_free(buffer);
4824 /* FIXME: Write real Estimated Size when we have it */
4825 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4827 GetLocalTime(&systime);
4828 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4829 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4831 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4832 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4834 buffer = msi_dup_property(package->db, szProductVersion);
4835 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4836 if (buffer)
4838 DWORD verdword = msi_version_str_to_dword(buffer);
4840 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4841 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4842 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4843 msi_free(buffer);
4846 return ERROR_SUCCESS;
4849 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4851 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4852 MSIRECORD *uirow;
4853 LPWSTR upgrade_code;
4854 HKEY hkey, props, upgrade_key;
4855 UINT rc;
4857 /* FIXME: also need to publish if the product is in advertise mode */
4858 if (!msi_check_publish(package))
4859 return ERROR_SUCCESS;
4861 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4862 if (rc != ERROR_SUCCESS)
4863 return rc;
4865 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4866 if (rc != ERROR_SUCCESS)
4867 goto done;
4869 rc = msi_publish_install_properties(package, hkey);
4870 if (rc != ERROR_SUCCESS)
4871 goto done;
4873 rc = msi_publish_install_properties(package, props);
4874 if (rc != ERROR_SUCCESS)
4875 goto done;
4877 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4878 if (upgrade_code)
4880 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4881 if (rc == ERROR_SUCCESS)
4883 squash_guid( package->ProductCode, squashed_pc );
4884 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4885 RegCloseKey( upgrade_key );
4887 msi_free( upgrade_code );
4889 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4890 package->delete_on_close = FALSE;
4892 done:
4893 uirow = MSI_CreateRecord( 1 );
4894 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4895 msi_ui_actiondata( package, szRegisterProduct, uirow );
4896 msiobj_release( &uirow->hdr );
4898 RegCloseKey(hkey);
4899 return ERROR_SUCCESS;
4902 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4904 return execute_script(package, SCRIPT_INSTALL);
4907 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4909 MSIPACKAGE *package = param;
4910 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4911 WCHAR *p, *icon_path;
4913 if (!icon) return ERROR_SUCCESS;
4914 if ((icon_path = msi_build_icon_path( package, icon )))
4916 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4917 DeleteFileW( icon_path );
4918 if ((p = strrchrW( icon_path, '\\' )))
4920 *p = 0;
4921 RemoveDirectoryW( icon_path );
4923 msi_free( icon_path );
4925 return ERROR_SUCCESS;
4928 static UINT msi_unpublish_icons( MSIPACKAGE *package )
4930 static const WCHAR query[]= {
4931 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
4932 MSIQUERY *view;
4933 UINT r;
4935 r = MSI_DatabaseOpenViewW( package->db, query, &view );
4936 if (r == ERROR_SUCCESS)
4938 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
4939 msiobj_release( &view->hdr );
4940 if (r != ERROR_SUCCESS)
4941 return r;
4943 return ERROR_SUCCESS;
4946 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
4948 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
4949 WCHAR *upgrade, **features;
4950 BOOL full_uninstall = TRUE;
4951 MSIFEATURE *feature;
4952 MSIPATCHINFO *patch;
4953 UINT i;
4955 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4957 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
4959 features = msi_split_string( remove, ',' );
4960 for (i = 0; features && features[i]; i++)
4962 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
4964 msi_free(features);
4966 if (!full_uninstall)
4967 return ERROR_SUCCESS;
4969 MSIREG_DeleteProductKey(package->ProductCode);
4970 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4971 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
4973 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4974 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4975 MSIREG_DeleteUserProductKey(package->ProductCode);
4976 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4978 upgrade = msi_dup_property(package->db, szUpgradeCode);
4979 if (upgrade)
4981 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4982 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
4983 msi_free(upgrade);
4986 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
4988 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
4989 if (!strcmpW( package->ProductCode, patch->products ))
4991 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
4992 patch->delete_on_close = TRUE;
4994 /* FIXME: remove local patch package if this is the last product */
4996 TRACE("removing local package %s\n", debugstr_w(package->localfile));
4997 package->delete_on_close = TRUE;
4999 msi_unpublish_icons( package );
5000 return ERROR_SUCCESS;
5003 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5005 UINT rc;
5006 WCHAR *remove;
5008 /* turn off scheduling */
5009 package->script->CurrentlyScripting= FALSE;
5011 /* first do the same as an InstallExecute */
5012 rc = ACTION_InstallExecute(package);
5013 if (rc != ERROR_SUCCESS)
5014 return rc;
5016 /* then handle commit actions */
5017 rc = execute_script(package, SCRIPT_COMMIT);
5018 if (rc != ERROR_SUCCESS)
5019 return rc;
5021 remove = msi_dup_property(package->db, szRemove);
5022 rc = msi_unpublish_product(package, remove);
5023 msi_free(remove);
5024 return rc;
5027 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5029 static const WCHAR RunOnce[] = {
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 'R','u','n','O','n','c','e',0};
5035 static const WCHAR InstallRunOnce[] = {
5036 'S','o','f','t','w','a','r','e','\\',
5037 'M','i','c','r','o','s','o','f','t','\\',
5038 'W','i','n','d','o','w','s','\\',
5039 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5040 'I','n','s','t','a','l','l','e','r','\\',
5041 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5043 static const WCHAR msiexec_fmt[] = {
5044 '%','s',
5045 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5046 '\"','%','s','\"',0};
5047 static const WCHAR install_fmt[] = {
5048 '/','I',' ','\"','%','s','\"',' ',
5049 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5050 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5051 WCHAR buffer[256], sysdir[MAX_PATH];
5052 HKEY hkey;
5053 WCHAR squished_pc[100];
5055 squash_guid(package->ProductCode,squished_pc);
5057 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5058 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5059 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5060 squished_pc);
5062 msi_reg_set_val_str( hkey, squished_pc, buffer );
5063 RegCloseKey(hkey);
5065 TRACE("Reboot command %s\n",debugstr_w(buffer));
5067 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5068 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5070 msi_reg_set_val_str( hkey, squished_pc, buffer );
5071 RegCloseKey(hkey);
5073 return ERROR_INSTALL_SUSPEND;
5076 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5078 static const WCHAR query[] =
5079 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5080 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5081 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5082 MSIRECORD *rec, *row;
5083 DWORD i, size = 0;
5084 va_list va;
5085 const WCHAR *str;
5086 WCHAR *data;
5088 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5090 rec = MSI_CreateRecord( count + 2 );
5091 str = MSI_RecordGetString( row, 1 );
5092 MSI_RecordSetStringW( rec, 0, str );
5093 msiobj_release( &row->hdr );
5094 MSI_RecordSetInteger( rec, 1, error );
5096 va_start( va, count );
5097 for (i = 0; i < count; i++)
5099 str = va_arg( va, const WCHAR *);
5100 MSI_RecordSetStringW( rec, i + 2, str );
5102 va_end( va );
5104 MSI_FormatRecordW( package, rec, NULL, &size );
5105 size++;
5106 data = msi_alloc( size * sizeof(WCHAR) );
5107 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5108 else data[0] = 0;
5109 msiobj_release( &rec->hdr );
5110 return data;
5113 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5115 DWORD attrib;
5116 UINT rc;
5119 * We are currently doing what should be done here in the top level Install
5120 * however for Administrative and uninstalls this step will be needed
5122 if (!package->PackagePath)
5123 return ERROR_SUCCESS;
5125 msi_set_sourcedir_props(package, TRUE);
5127 attrib = GetFileAttributesW(package->db->path);
5128 if (attrib == INVALID_FILE_ATTRIBUTES)
5130 LPWSTR prompt, msg;
5131 DWORD size = 0;
5133 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5134 package->Context, MSICODE_PRODUCT,
5135 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5136 if (rc == ERROR_MORE_DATA)
5138 prompt = msi_alloc(size * sizeof(WCHAR));
5139 MsiSourceListGetInfoW(package->ProductCode, NULL,
5140 package->Context, MSICODE_PRODUCT,
5141 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5143 else
5144 prompt = strdupW(package->db->path);
5146 msg = msi_build_error_string(package, 1302, 1, prompt);
5147 msi_free(prompt);
5148 while(attrib == INVALID_FILE_ATTRIBUTES)
5150 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5151 if (rc == IDCANCEL)
5153 msi_free(msg);
5154 return ERROR_INSTALL_USEREXIT;
5156 attrib = GetFileAttributesW(package->db->path);
5158 msi_free(msg);
5159 rc = ERROR_SUCCESS;
5161 else
5162 return ERROR_SUCCESS;
5164 return rc;
5167 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5169 HKEY hkey = 0;
5170 LPWSTR buffer, productid = NULL;
5171 UINT i, rc = ERROR_SUCCESS;
5172 MSIRECORD *uirow;
5174 static const WCHAR szPropKeys[][80] =
5176 {'P','r','o','d','u','c','t','I','D',0},
5177 {'U','S','E','R','N','A','M','E',0},
5178 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5179 {0},
5182 static const WCHAR szRegKeys[][80] =
5184 {'P','r','o','d','u','c','t','I','D',0},
5185 {'R','e','g','O','w','n','e','r',0},
5186 {'R','e','g','C','o','m','p','a','n','y',0},
5187 {0},
5190 if (msi_check_unpublish(package))
5192 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5193 goto end;
5196 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5197 if (!productid)
5198 goto end;
5200 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5201 NULL, &hkey, TRUE);
5202 if (rc != ERROR_SUCCESS)
5203 goto end;
5205 for( i = 0; szPropKeys[i][0]; i++ )
5207 buffer = msi_dup_property( package->db, szPropKeys[i] );
5208 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5209 msi_free( buffer );
5212 end:
5213 uirow = MSI_CreateRecord( 1 );
5214 MSI_RecordSetStringW( uirow, 1, productid );
5215 msi_ui_actiondata( package, szRegisterUser, uirow );
5216 msiobj_release( &uirow->hdr );
5218 msi_free(productid);
5219 RegCloseKey(hkey);
5220 return rc;
5224 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5226 UINT rc;
5228 package->script->InWhatSequence |= SEQUENCE_EXEC;
5229 rc = ACTION_ProcessExecSequence(package,FALSE);
5230 return rc;
5233 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5235 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5236 WCHAR productid_85[21], component_85[21], *ret;
5237 GUID clsid;
5238 DWORD sz;
5240 /* > is used if there is a component GUID and < if not. */
5242 productid_85[0] = 0;
5243 component_85[0] = 0;
5244 CLSIDFromString( package->ProductCode, &clsid );
5246 encode_base85_guid( &clsid, productid_85 );
5247 if (component)
5249 CLSIDFromString( component->ComponentId, &clsid );
5250 encode_base85_guid( &clsid, component_85 );
5253 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5254 debugstr_w(component_85));
5256 sz = 20 + strlenW( feature ) + 20 + 3;
5257 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5258 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5259 return ret;
5262 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5264 MSIPACKAGE *package = param;
5265 LPCWSTR compgroupid, component, feature, qualifier, text;
5266 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5267 HKEY hkey = NULL;
5268 UINT rc;
5269 MSICOMPONENT *comp;
5270 MSIFEATURE *feat;
5271 DWORD sz;
5272 MSIRECORD *uirow;
5273 int len;
5275 feature = MSI_RecordGetString(rec, 5);
5276 feat = msi_get_loaded_feature(package, feature);
5277 if (!feat)
5278 return ERROR_SUCCESS;
5280 feat->Action = msi_get_feature_action( package, feat );
5281 if (feat->Action != INSTALLSTATE_LOCAL &&
5282 feat->Action != INSTALLSTATE_SOURCE &&
5283 feat->Action != INSTALLSTATE_ADVERTISED)
5285 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5286 return ERROR_SUCCESS;
5289 component = MSI_RecordGetString(rec, 3);
5290 comp = msi_get_loaded_component(package, component);
5291 if (!comp)
5292 return ERROR_SUCCESS;
5294 compgroupid = MSI_RecordGetString(rec,1);
5295 qualifier = MSI_RecordGetString(rec,2);
5297 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5298 if (rc != ERROR_SUCCESS)
5299 goto end;
5301 advertise = msi_create_component_advertise_string( package, comp, feature );
5302 text = MSI_RecordGetString( rec, 4 );
5303 if (text)
5305 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5306 strcpyW( p, advertise );
5307 strcatW( p, text );
5308 msi_free( advertise );
5309 advertise = p;
5311 existing = msi_reg_get_val_str( hkey, qualifier );
5313 sz = strlenW( advertise ) + 1;
5314 if (existing)
5316 for (p = existing; *p; p += len)
5318 len = strlenW( p ) + 1;
5319 if (strcmpW( advertise, p )) sz += len;
5322 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5324 rc = ERROR_OUTOFMEMORY;
5325 goto end;
5327 q = output;
5328 if (existing)
5330 for (p = existing; *p; p += len)
5332 len = strlenW( p ) + 1;
5333 if (strcmpW( advertise, p ))
5335 memcpy( q, p, len * sizeof(WCHAR) );
5336 q += len;
5340 strcpyW( q, advertise );
5341 q[strlenW( q ) + 1] = 0;
5343 msi_reg_set_val_multi_str( hkey, qualifier, output );
5345 end:
5346 RegCloseKey(hkey);
5347 msi_free( output );
5348 msi_free( advertise );
5349 msi_free( existing );
5351 /* the UI chunk */
5352 uirow = MSI_CreateRecord( 2 );
5353 MSI_RecordSetStringW( uirow, 1, compgroupid );
5354 MSI_RecordSetStringW( uirow, 2, qualifier);
5355 msi_ui_actiondata( package, szPublishComponents, uirow );
5356 msiobj_release( &uirow->hdr );
5357 /* FIXME: call ui_progress? */
5359 return rc;
5363 * At present I am ignorning the advertised components part of this and only
5364 * focusing on the qualified component sets
5366 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5368 static const WCHAR query[] = {
5369 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5370 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5371 MSIQUERY *view;
5372 UINT rc;
5374 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5375 if (rc != ERROR_SUCCESS)
5376 return ERROR_SUCCESS;
5378 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5379 msiobj_release(&view->hdr);
5380 return rc;
5383 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5385 static const WCHAR szInstallerComponents[] = {
5386 'S','o','f','t','w','a','r','e','\\',
5387 'M','i','c','r','o','s','o','f','t','\\',
5388 'I','n','s','t','a','l','l','e','r','\\',
5389 'C','o','m','p','o','n','e','n','t','s','\\',0};
5391 MSIPACKAGE *package = param;
5392 LPCWSTR compgroupid, component, feature, qualifier;
5393 MSICOMPONENT *comp;
5394 MSIFEATURE *feat;
5395 MSIRECORD *uirow;
5396 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5397 LONG res;
5399 feature = MSI_RecordGetString( rec, 5 );
5400 feat = msi_get_loaded_feature( package, feature );
5401 if (!feat)
5402 return ERROR_SUCCESS;
5404 feat->Action = msi_get_feature_action( package, feat );
5405 if (feat->Action != INSTALLSTATE_ABSENT)
5407 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5408 return ERROR_SUCCESS;
5411 component = MSI_RecordGetString( rec, 3 );
5412 comp = msi_get_loaded_component( package, component );
5413 if (!comp)
5414 return ERROR_SUCCESS;
5416 compgroupid = MSI_RecordGetString( rec, 1 );
5417 qualifier = MSI_RecordGetString( rec, 2 );
5419 squash_guid( compgroupid, squashed );
5420 strcpyW( keypath, szInstallerComponents );
5421 strcatW( keypath, squashed );
5423 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5424 if (res != ERROR_SUCCESS)
5426 WARN("Unable to delete component key %d\n", res);
5429 uirow = MSI_CreateRecord( 2 );
5430 MSI_RecordSetStringW( uirow, 1, compgroupid );
5431 MSI_RecordSetStringW( uirow, 2, qualifier );
5432 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5433 msiobj_release( &uirow->hdr );
5435 return ERROR_SUCCESS;
5438 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5440 static const WCHAR query[] = {
5441 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5442 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5443 MSIQUERY *view;
5444 UINT rc;
5446 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5447 if (rc != ERROR_SUCCESS)
5448 return ERROR_SUCCESS;
5450 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5451 msiobj_release( &view->hdr );
5452 return rc;
5455 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5457 static const WCHAR query[] =
5458 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5459 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5460 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5461 MSIPACKAGE *package = param;
5462 MSICOMPONENT *component;
5463 MSIRECORD *row;
5464 MSIFILE *file;
5465 SC_HANDLE hscm = NULL, service = NULL;
5466 LPCWSTR comp, key;
5467 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5468 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5469 DWORD serv_type, start_type, err_control;
5470 SERVICE_DESCRIPTIONW sd = {NULL};
5472 comp = MSI_RecordGetString( rec, 12 );
5473 component = msi_get_loaded_component( package, comp );
5474 if (!component)
5476 WARN("service component not found\n");
5477 goto done;
5479 component->Action = msi_get_component_action( package, component );
5480 if (component->Action != INSTALLSTATE_LOCAL)
5482 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5483 goto done;
5485 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5486 if (!hscm)
5488 ERR("Failed to open the SC Manager!\n");
5489 goto done;
5492 start_type = MSI_RecordGetInteger(rec, 5);
5493 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5494 goto done;
5496 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5497 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5498 serv_type = MSI_RecordGetInteger(rec, 4);
5499 err_control = MSI_RecordGetInteger(rec, 6);
5500 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5501 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5502 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5503 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5504 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5505 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5507 /* fetch the service path */
5508 row = MSI_QueryGetRecord(package->db, query, comp);
5509 if (!row)
5511 ERR("Query failed\n");
5512 goto done;
5514 key = MSI_RecordGetString(row, 6);
5515 file = msi_get_loaded_file(package, key);
5516 msiobj_release(&row->hdr);
5517 if (!file)
5519 ERR("Failed to load the service file\n");
5520 goto done;
5523 if (!args || !args[0]) image_path = file->TargetPath;
5524 else
5526 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5527 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5528 return ERROR_OUTOFMEMORY;
5530 strcpyW(image_path, file->TargetPath);
5531 strcatW(image_path, szSpace);
5532 strcatW(image_path, args);
5534 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5535 start_type, err_control, image_path, load_order,
5536 NULL, depends, serv_name, pass);
5538 if (!service)
5540 if (GetLastError() != ERROR_SERVICE_EXISTS)
5541 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5543 else if (sd.lpDescription)
5545 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5546 WARN("failed to set service description %u\n", GetLastError());
5549 if (image_path != file->TargetPath) msi_free(image_path);
5550 done:
5551 CloseServiceHandle(service);
5552 CloseServiceHandle(hscm);
5553 msi_free(name);
5554 msi_free(disp);
5555 msi_free(sd.lpDescription);
5556 msi_free(load_order);
5557 msi_free(serv_name);
5558 msi_free(pass);
5559 msi_free(depends);
5560 msi_free(args);
5562 return ERROR_SUCCESS;
5565 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5567 static const WCHAR query[] = {
5568 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5569 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5570 MSIQUERY *view;
5571 UINT rc;
5573 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5574 if (rc != ERROR_SUCCESS)
5575 return ERROR_SUCCESS;
5577 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5578 msiobj_release(&view->hdr);
5579 return rc;
5582 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5583 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5585 LPCWSTR *vector, *temp_vector;
5586 LPWSTR p, q;
5587 DWORD sep_len;
5589 static const WCHAR separator[] = {'[','~',']',0};
5591 *numargs = 0;
5592 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5594 if (!args)
5595 return NULL;
5597 vector = msi_alloc(sizeof(LPWSTR));
5598 if (!vector)
5599 return NULL;
5601 p = args;
5604 (*numargs)++;
5605 vector[*numargs - 1] = p;
5607 if ((q = strstrW(p, separator)))
5609 *q = '\0';
5611 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5612 if (!temp_vector)
5614 msi_free(vector);
5615 return NULL;
5617 vector = temp_vector;
5619 p = q + sep_len;
5621 } while (q);
5623 return vector;
5626 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5628 MSIPACKAGE *package = param;
5629 MSICOMPONENT *comp;
5630 MSIRECORD *uirow;
5631 SC_HANDLE scm = NULL, service = NULL;
5632 LPCWSTR component, *vector = NULL;
5633 LPWSTR name, args, display_name = NULL;
5634 DWORD event, numargs, len, wait, dummy;
5635 UINT r = ERROR_FUNCTION_FAILED;
5636 SERVICE_STATUS_PROCESS status;
5637 ULONGLONG start_time;
5639 component = MSI_RecordGetString(rec, 6);
5640 comp = msi_get_loaded_component(package, component);
5641 if (!comp)
5642 return ERROR_SUCCESS;
5644 comp->Action = msi_get_component_action( package, comp );
5645 if (comp->Action != INSTALLSTATE_LOCAL)
5647 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5648 return ERROR_SUCCESS;
5651 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5652 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5653 event = MSI_RecordGetInteger(rec, 3);
5654 wait = MSI_RecordGetInteger(rec, 5);
5656 if (!(event & msidbServiceControlEventStart))
5658 r = ERROR_SUCCESS;
5659 goto done;
5662 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5663 if (!scm)
5665 ERR("Failed to open the service control manager\n");
5666 goto done;
5669 len = 0;
5670 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5671 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5673 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5674 GetServiceDisplayNameW( scm, name, display_name, &len );
5677 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5678 if (!service)
5680 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5681 goto done;
5684 vector = msi_service_args_to_vector(args, &numargs);
5686 if (!StartServiceW(service, numargs, vector) &&
5687 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5689 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5690 goto done;
5693 r = ERROR_SUCCESS;
5694 if (wait)
5696 /* wait for at most 30 seconds for the service to be up and running */
5697 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5698 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5700 TRACE("failed to query service status (%u)\n", GetLastError());
5701 goto done;
5703 start_time = GetTickCount64();
5704 while (status.dwCurrentState == SERVICE_START_PENDING)
5706 if (GetTickCount64() - start_time > 30000) break;
5707 Sleep(1000);
5708 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5709 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5711 TRACE("failed to query service status (%u)\n", GetLastError());
5712 goto done;
5715 if (status.dwCurrentState != SERVICE_RUNNING)
5717 WARN("service failed to start %u\n", status.dwCurrentState);
5718 r = ERROR_FUNCTION_FAILED;
5722 done:
5723 uirow = MSI_CreateRecord( 2 );
5724 MSI_RecordSetStringW( uirow, 1, display_name );
5725 MSI_RecordSetStringW( uirow, 2, name );
5726 msi_ui_actiondata( package, szStartServices, uirow );
5727 msiobj_release( &uirow->hdr );
5729 CloseServiceHandle(service);
5730 CloseServiceHandle(scm);
5732 msi_free(name);
5733 msi_free(args);
5734 msi_free(vector);
5735 msi_free(display_name);
5736 return r;
5739 static UINT ACTION_StartServices( MSIPACKAGE *package )
5741 static const WCHAR query[] = {
5742 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5743 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5744 MSIQUERY *view;
5745 UINT rc;
5747 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5748 if (rc != ERROR_SUCCESS)
5749 return ERROR_SUCCESS;
5751 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5752 msiobj_release(&view->hdr);
5753 return rc;
5756 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5758 DWORD i, needed, count;
5759 ENUM_SERVICE_STATUSW *dependencies;
5760 SERVICE_STATUS ss;
5761 SC_HANDLE depserv;
5763 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5764 0, &needed, &count))
5765 return TRUE;
5767 if (GetLastError() != ERROR_MORE_DATA)
5768 return FALSE;
5770 dependencies = msi_alloc(needed);
5771 if (!dependencies)
5772 return FALSE;
5774 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5775 needed, &needed, &count))
5776 goto error;
5778 for (i = 0; i < count; i++)
5780 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5781 SERVICE_STOP | SERVICE_QUERY_STATUS);
5782 if (!depserv)
5783 goto error;
5785 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5786 goto error;
5789 return TRUE;
5791 error:
5792 msi_free(dependencies);
5793 return FALSE;
5796 static UINT stop_service( LPCWSTR name )
5798 SC_HANDLE scm = NULL, service = NULL;
5799 SERVICE_STATUS status;
5800 SERVICE_STATUS_PROCESS ssp;
5801 DWORD needed;
5803 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5804 if (!scm)
5806 WARN("Failed to open the SCM: %d\n", GetLastError());
5807 goto done;
5810 service = OpenServiceW(scm, name,
5811 SERVICE_STOP |
5812 SERVICE_QUERY_STATUS |
5813 SERVICE_ENUMERATE_DEPENDENTS);
5814 if (!service)
5816 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5817 goto done;
5820 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5821 sizeof(SERVICE_STATUS_PROCESS), &needed))
5823 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5824 goto done;
5827 if (ssp.dwCurrentState == SERVICE_STOPPED)
5828 goto done;
5830 stop_service_dependents(scm, service);
5832 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5833 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5835 done:
5836 CloseServiceHandle(service);
5837 CloseServiceHandle(scm);
5839 return ERROR_SUCCESS;
5842 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5844 MSIPACKAGE *package = param;
5845 MSICOMPONENT *comp;
5846 MSIRECORD *uirow;
5847 LPCWSTR component;
5848 LPWSTR name = NULL, display_name = NULL;
5849 DWORD event, len;
5850 SC_HANDLE scm;
5852 event = MSI_RecordGetInteger( rec, 3 );
5853 if (!(event & msidbServiceControlEventStop))
5854 return ERROR_SUCCESS;
5856 component = MSI_RecordGetString( rec, 6 );
5857 comp = msi_get_loaded_component( package, component );
5858 if (!comp)
5859 return ERROR_SUCCESS;
5861 comp->Action = msi_get_component_action( package, comp );
5862 if (comp->Action != INSTALLSTATE_ABSENT)
5864 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5865 return ERROR_SUCCESS;
5868 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5869 if (!scm)
5871 ERR("Failed to open the service control manager\n");
5872 goto done;
5875 len = 0;
5876 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5877 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5879 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5880 GetServiceDisplayNameW( scm, name, display_name, &len );
5882 CloseServiceHandle( scm );
5884 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5885 stop_service( name );
5887 done:
5888 uirow = MSI_CreateRecord( 2 );
5889 MSI_RecordSetStringW( uirow, 1, display_name );
5890 MSI_RecordSetStringW( uirow, 2, name );
5891 msi_ui_actiondata( package, szStopServices, uirow );
5892 msiobj_release( &uirow->hdr );
5894 msi_free( name );
5895 msi_free( display_name );
5896 return ERROR_SUCCESS;
5899 static UINT ACTION_StopServices( MSIPACKAGE *package )
5901 static const WCHAR query[] = {
5902 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5903 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5904 MSIQUERY *view;
5905 UINT rc;
5907 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5908 if (rc != ERROR_SUCCESS)
5909 return ERROR_SUCCESS;
5911 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5912 msiobj_release(&view->hdr);
5913 return rc;
5916 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5918 MSIPACKAGE *package = param;
5919 MSICOMPONENT *comp;
5920 MSIRECORD *uirow;
5921 LPWSTR name = NULL, display_name = NULL;
5922 DWORD event, len;
5923 SC_HANDLE scm = NULL, service = NULL;
5925 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
5926 if (!comp)
5927 return ERROR_SUCCESS;
5929 event = MSI_RecordGetInteger( rec, 3 );
5930 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
5932 comp->Action = msi_get_component_action( package, comp );
5933 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
5934 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
5936 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
5937 msi_free( name );
5938 return ERROR_SUCCESS;
5940 stop_service( name );
5942 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
5943 if (!scm)
5945 WARN("Failed to open the SCM: %d\n", GetLastError());
5946 goto done;
5949 len = 0;
5950 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5951 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5953 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5954 GetServiceDisplayNameW( scm, name, display_name, &len );
5957 service = OpenServiceW( scm, name, DELETE );
5958 if (!service)
5960 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
5961 goto done;
5964 if (!DeleteService( service ))
5965 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
5967 done:
5968 uirow = MSI_CreateRecord( 2 );
5969 MSI_RecordSetStringW( uirow, 1, display_name );
5970 MSI_RecordSetStringW( uirow, 2, name );
5971 msi_ui_actiondata( package, szDeleteServices, uirow );
5972 msiobj_release( &uirow->hdr );
5974 CloseServiceHandle( service );
5975 CloseServiceHandle( scm );
5976 msi_free( name );
5977 msi_free( display_name );
5979 return ERROR_SUCCESS;
5982 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
5984 static const WCHAR query[] = {
5985 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5986 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5987 MSIQUERY *view;
5988 UINT rc;
5990 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5991 if (rc != ERROR_SUCCESS)
5992 return ERROR_SUCCESS;
5994 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
5995 msiobj_release( &view->hdr );
5996 return rc;
5999 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6001 MSIPACKAGE *package = param;
6002 LPWSTR driver, driver_path, ptr;
6003 WCHAR outpath[MAX_PATH];
6004 MSIFILE *driver_file = NULL, *setup_file = NULL;
6005 MSICOMPONENT *comp;
6006 MSIRECORD *uirow;
6007 LPCWSTR desc, file_key, component;
6008 DWORD len, usage;
6009 UINT r = ERROR_SUCCESS;
6011 static const WCHAR driver_fmt[] = {
6012 'D','r','i','v','e','r','=','%','s',0};
6013 static const WCHAR setup_fmt[] = {
6014 'S','e','t','u','p','=','%','s',0};
6015 static const WCHAR usage_fmt[] = {
6016 'F','i','l','e','U','s','a','g','e','=','1',0};
6018 component = MSI_RecordGetString( rec, 2 );
6019 comp = msi_get_loaded_component( package, component );
6020 if (!comp)
6021 return ERROR_SUCCESS;
6023 comp->Action = msi_get_component_action( package, comp );
6024 if (comp->Action != INSTALLSTATE_LOCAL)
6026 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6027 return ERROR_SUCCESS;
6029 desc = MSI_RecordGetString(rec, 3);
6031 file_key = MSI_RecordGetString( rec, 4 );
6032 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6034 file_key = MSI_RecordGetString( rec, 5 );
6035 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6037 if (!driver_file)
6039 ERR("ODBC Driver entry not found!\n");
6040 return ERROR_FUNCTION_FAILED;
6043 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6044 if (setup_file)
6045 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6046 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6048 driver = msi_alloc(len * sizeof(WCHAR));
6049 if (!driver)
6050 return ERROR_OUTOFMEMORY;
6052 ptr = driver;
6053 lstrcpyW(ptr, desc);
6054 ptr += lstrlenW(ptr) + 1;
6056 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6057 ptr += len + 1;
6059 if (setup_file)
6061 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6062 ptr += len + 1;
6065 lstrcpyW(ptr, usage_fmt);
6066 ptr += lstrlenW(ptr) + 1;
6067 *ptr = '\0';
6069 driver_path = strdupW(driver_file->TargetPath);
6070 ptr = strrchrW(driver_path, '\\');
6071 if (ptr) *ptr = '\0';
6073 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6074 NULL, ODBC_INSTALL_COMPLETE, &usage))
6076 ERR("Failed to install SQL driver!\n");
6077 r = ERROR_FUNCTION_FAILED;
6080 uirow = MSI_CreateRecord( 5 );
6081 MSI_RecordSetStringW( uirow, 1, desc );
6082 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6083 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6084 msi_ui_actiondata( package, szInstallODBC, uirow );
6085 msiobj_release( &uirow->hdr );
6087 msi_free(driver);
6088 msi_free(driver_path);
6090 return r;
6093 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6095 MSIPACKAGE *package = param;
6096 LPWSTR translator, translator_path, ptr;
6097 WCHAR outpath[MAX_PATH];
6098 MSIFILE *translator_file = NULL, *setup_file = NULL;
6099 MSICOMPONENT *comp;
6100 MSIRECORD *uirow;
6101 LPCWSTR desc, file_key, component;
6102 DWORD len, usage;
6103 UINT r = ERROR_SUCCESS;
6105 static const WCHAR translator_fmt[] = {
6106 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6107 static const WCHAR setup_fmt[] = {
6108 'S','e','t','u','p','=','%','s',0};
6110 component = MSI_RecordGetString( rec, 2 );
6111 comp = msi_get_loaded_component( package, component );
6112 if (!comp)
6113 return ERROR_SUCCESS;
6115 comp->Action = msi_get_component_action( package, comp );
6116 if (comp->Action != INSTALLSTATE_LOCAL)
6118 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6119 return ERROR_SUCCESS;
6121 desc = MSI_RecordGetString(rec, 3);
6123 file_key = MSI_RecordGetString( rec, 4 );
6124 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6126 file_key = MSI_RecordGetString( rec, 5 );
6127 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6129 if (!translator_file)
6131 ERR("ODBC Translator entry not found!\n");
6132 return ERROR_FUNCTION_FAILED;
6135 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6136 if (setup_file)
6137 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6139 translator = msi_alloc(len * sizeof(WCHAR));
6140 if (!translator)
6141 return ERROR_OUTOFMEMORY;
6143 ptr = translator;
6144 lstrcpyW(ptr, desc);
6145 ptr += lstrlenW(ptr) + 1;
6147 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6148 ptr += len + 1;
6150 if (setup_file)
6152 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6153 ptr += len + 1;
6155 *ptr = '\0';
6157 translator_path = strdupW(translator_file->TargetPath);
6158 ptr = strrchrW(translator_path, '\\');
6159 if (ptr) *ptr = '\0';
6161 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6162 NULL, ODBC_INSTALL_COMPLETE, &usage))
6164 ERR("Failed to install SQL translator!\n");
6165 r = ERROR_FUNCTION_FAILED;
6168 uirow = MSI_CreateRecord( 5 );
6169 MSI_RecordSetStringW( uirow, 1, desc );
6170 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6171 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6172 msi_ui_actiondata( package, szInstallODBC, uirow );
6173 msiobj_release( &uirow->hdr );
6175 msi_free(translator);
6176 msi_free(translator_path);
6178 return r;
6181 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6183 MSIPACKAGE *package = param;
6184 MSICOMPONENT *comp;
6185 LPWSTR attrs;
6186 LPCWSTR desc, driver, component;
6187 WORD request = ODBC_ADD_SYS_DSN;
6188 INT registration;
6189 DWORD len;
6190 UINT r = ERROR_SUCCESS;
6191 MSIRECORD *uirow;
6193 static const WCHAR attrs_fmt[] = {
6194 'D','S','N','=','%','s',0 };
6196 component = MSI_RecordGetString( rec, 2 );
6197 comp = msi_get_loaded_component( package, component );
6198 if (!comp)
6199 return ERROR_SUCCESS;
6201 comp->Action = msi_get_component_action( package, comp );
6202 if (comp->Action != INSTALLSTATE_LOCAL)
6204 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6205 return ERROR_SUCCESS;
6208 desc = MSI_RecordGetString(rec, 3);
6209 driver = MSI_RecordGetString(rec, 4);
6210 registration = MSI_RecordGetInteger(rec, 5);
6212 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6213 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6215 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6216 attrs = msi_alloc(len * sizeof(WCHAR));
6217 if (!attrs)
6218 return ERROR_OUTOFMEMORY;
6220 len = sprintfW(attrs, attrs_fmt, desc);
6221 attrs[len + 1] = 0;
6223 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6225 ERR("Failed to install SQL data source!\n");
6226 r = ERROR_FUNCTION_FAILED;
6229 uirow = MSI_CreateRecord( 5 );
6230 MSI_RecordSetStringW( uirow, 1, desc );
6231 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6232 MSI_RecordSetInteger( uirow, 3, request );
6233 msi_ui_actiondata( package, szInstallODBC, uirow );
6234 msiobj_release( &uirow->hdr );
6236 msi_free(attrs);
6238 return r;
6241 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6243 static const WCHAR driver_query[] = {
6244 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6245 'O','D','B','C','D','r','i','v','e','r',0};
6246 static const WCHAR translator_query[] = {
6247 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6248 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6249 static const WCHAR source_query[] = {
6250 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6251 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6252 MSIQUERY *view;
6253 UINT rc;
6255 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6256 if (rc == ERROR_SUCCESS)
6258 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6259 msiobj_release(&view->hdr);
6260 if (rc != ERROR_SUCCESS)
6261 return rc;
6263 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6264 if (rc == ERROR_SUCCESS)
6266 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6267 msiobj_release(&view->hdr);
6268 if (rc != ERROR_SUCCESS)
6269 return rc;
6271 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6272 if (rc == ERROR_SUCCESS)
6274 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6275 msiobj_release(&view->hdr);
6276 if (rc != ERROR_SUCCESS)
6277 return rc;
6279 return ERROR_SUCCESS;
6282 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6284 MSIPACKAGE *package = param;
6285 MSICOMPONENT *comp;
6286 MSIRECORD *uirow;
6287 DWORD usage;
6288 LPCWSTR desc, component;
6290 component = MSI_RecordGetString( rec, 2 );
6291 comp = msi_get_loaded_component( package, component );
6292 if (!comp)
6293 return ERROR_SUCCESS;
6295 comp->Action = msi_get_component_action( package, comp );
6296 if (comp->Action != INSTALLSTATE_ABSENT)
6298 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6299 return ERROR_SUCCESS;
6302 desc = MSI_RecordGetString( rec, 3 );
6303 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6305 WARN("Failed to remove ODBC driver\n");
6307 else if (!usage)
6309 FIXME("Usage count reached 0\n");
6312 uirow = MSI_CreateRecord( 2 );
6313 MSI_RecordSetStringW( uirow, 1, desc );
6314 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6315 msi_ui_actiondata( package, szRemoveODBC, uirow );
6316 msiobj_release( &uirow->hdr );
6318 return ERROR_SUCCESS;
6321 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6323 MSIPACKAGE *package = param;
6324 MSICOMPONENT *comp;
6325 MSIRECORD *uirow;
6326 DWORD usage;
6327 LPCWSTR desc, component;
6329 component = MSI_RecordGetString( rec, 2 );
6330 comp = msi_get_loaded_component( package, component );
6331 if (!comp)
6332 return ERROR_SUCCESS;
6334 comp->Action = msi_get_component_action( package, comp );
6335 if (comp->Action != INSTALLSTATE_ABSENT)
6337 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6338 return ERROR_SUCCESS;
6341 desc = MSI_RecordGetString( rec, 3 );
6342 if (!SQLRemoveTranslatorW( desc, &usage ))
6344 WARN("Failed to remove ODBC translator\n");
6346 else if (!usage)
6348 FIXME("Usage count reached 0\n");
6351 uirow = MSI_CreateRecord( 2 );
6352 MSI_RecordSetStringW( uirow, 1, desc );
6353 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6354 msi_ui_actiondata( package, szRemoveODBC, uirow );
6355 msiobj_release( &uirow->hdr );
6357 return ERROR_SUCCESS;
6360 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6362 MSIPACKAGE *package = param;
6363 MSICOMPONENT *comp;
6364 MSIRECORD *uirow;
6365 LPWSTR attrs;
6366 LPCWSTR desc, driver, component;
6367 WORD request = ODBC_REMOVE_SYS_DSN;
6368 INT registration;
6369 DWORD len;
6371 static const WCHAR attrs_fmt[] = {
6372 'D','S','N','=','%','s',0 };
6374 component = MSI_RecordGetString( rec, 2 );
6375 comp = msi_get_loaded_component( package, component );
6376 if (!comp)
6377 return ERROR_SUCCESS;
6379 comp->Action = msi_get_component_action( package, comp );
6380 if (comp->Action != INSTALLSTATE_ABSENT)
6382 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6383 return ERROR_SUCCESS;
6386 desc = MSI_RecordGetString( rec, 3 );
6387 driver = MSI_RecordGetString( rec, 4 );
6388 registration = MSI_RecordGetInteger( rec, 5 );
6390 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6391 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6393 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6394 attrs = msi_alloc( len * sizeof(WCHAR) );
6395 if (!attrs)
6396 return ERROR_OUTOFMEMORY;
6398 FIXME("Use ODBCSourceAttribute table\n");
6400 len = sprintfW( attrs, attrs_fmt, desc );
6401 attrs[len + 1] = 0;
6403 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6405 WARN("Failed to remove ODBC data source\n");
6407 msi_free( attrs );
6409 uirow = MSI_CreateRecord( 3 );
6410 MSI_RecordSetStringW( uirow, 1, desc );
6411 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6412 MSI_RecordSetInteger( uirow, 3, request );
6413 msi_ui_actiondata( package, szRemoveODBC, uirow );
6414 msiobj_release( &uirow->hdr );
6416 return ERROR_SUCCESS;
6419 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6421 static const WCHAR driver_query[] = {
6422 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6423 'O','D','B','C','D','r','i','v','e','r',0};
6424 static const WCHAR translator_query[] = {
6425 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6426 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6427 static const WCHAR source_query[] = {
6428 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6429 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6430 MSIQUERY *view;
6431 UINT rc;
6433 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6434 if (rc == ERROR_SUCCESS)
6436 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6437 msiobj_release( &view->hdr );
6438 if (rc != ERROR_SUCCESS)
6439 return rc;
6441 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6442 if (rc == ERROR_SUCCESS)
6444 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6445 msiobj_release( &view->hdr );
6446 if (rc != ERROR_SUCCESS)
6447 return rc;
6449 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6450 if (rc == ERROR_SUCCESS)
6452 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6453 msiobj_release( &view->hdr );
6454 if (rc != ERROR_SUCCESS)
6455 return rc;
6457 return ERROR_SUCCESS;
6460 #define ENV_ACT_SETALWAYS 0x1
6461 #define ENV_ACT_SETABSENT 0x2
6462 #define ENV_ACT_REMOVE 0x4
6463 #define ENV_ACT_REMOVEMATCH 0x8
6465 #define ENV_MOD_MACHINE 0x20000000
6466 #define ENV_MOD_APPEND 0x40000000
6467 #define ENV_MOD_PREFIX 0x80000000
6468 #define ENV_MOD_MASK 0xC0000000
6470 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6472 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6474 LPCWSTR cptr = *name;
6476 static const WCHAR prefix[] = {'[','~',']',0};
6477 static const int prefix_len = 3;
6479 *flags = 0;
6480 while (*cptr)
6482 if (*cptr == '=')
6483 *flags |= ENV_ACT_SETALWAYS;
6484 else if (*cptr == '+')
6485 *flags |= ENV_ACT_SETABSENT;
6486 else if (*cptr == '-')
6487 *flags |= ENV_ACT_REMOVE;
6488 else if (*cptr == '!')
6489 *flags |= ENV_ACT_REMOVEMATCH;
6490 else if (*cptr == '*')
6491 *flags |= ENV_MOD_MACHINE;
6492 else
6493 break;
6495 cptr++;
6496 (*name)++;
6499 if (!*cptr)
6501 ERR("Missing environment variable\n");
6502 return ERROR_FUNCTION_FAILED;
6505 if (*value)
6507 LPCWSTR ptr = *value;
6508 if (!strncmpW(ptr, prefix, prefix_len))
6510 if (ptr[prefix_len] == szSemiColon[0])
6512 *flags |= ENV_MOD_APPEND;
6513 *value += lstrlenW(prefix);
6515 else
6517 *value = NULL;
6520 else if (lstrlenW(*value) >= prefix_len)
6522 ptr += lstrlenW(ptr) - prefix_len;
6523 if (!strcmpW( ptr, prefix ))
6525 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6527 *flags |= ENV_MOD_PREFIX;
6528 /* the "[~]" will be removed by deformat_string */;
6530 else
6532 *value = NULL;
6538 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6539 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6540 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6541 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6543 ERR("Invalid flags: %08x\n", *flags);
6544 return ERROR_FUNCTION_FAILED;
6547 if (!*flags)
6548 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6550 return ERROR_SUCCESS;
6553 static UINT open_env_key( DWORD flags, HKEY *key )
6555 static const WCHAR user_env[] =
6556 {'E','n','v','i','r','o','n','m','e','n','t',0};
6557 static const WCHAR machine_env[] =
6558 {'S','y','s','t','e','m','\\',
6559 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6560 'C','o','n','t','r','o','l','\\',
6561 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6562 'E','n','v','i','r','o','n','m','e','n','t',0};
6563 const WCHAR *env;
6564 HKEY root;
6565 LONG res;
6567 if (flags & ENV_MOD_MACHINE)
6569 env = machine_env;
6570 root = HKEY_LOCAL_MACHINE;
6572 else
6574 env = user_env;
6575 root = HKEY_CURRENT_USER;
6578 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6579 if (res != ERROR_SUCCESS)
6581 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6582 return ERROR_FUNCTION_FAILED;
6585 return ERROR_SUCCESS;
6588 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6590 MSIPACKAGE *package = param;
6591 LPCWSTR name, value, component;
6592 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6593 DWORD flags, type, size;
6594 UINT res;
6595 HKEY env = NULL;
6596 MSICOMPONENT *comp;
6597 MSIRECORD *uirow;
6598 int action = 0;
6600 component = MSI_RecordGetString(rec, 4);
6601 comp = msi_get_loaded_component(package, component);
6602 if (!comp)
6603 return ERROR_SUCCESS;
6605 comp->Action = msi_get_component_action( package, comp );
6606 if (comp->Action != INSTALLSTATE_LOCAL)
6608 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6609 return ERROR_SUCCESS;
6611 name = MSI_RecordGetString(rec, 2);
6612 value = MSI_RecordGetString(rec, 3);
6614 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6616 res = env_parse_flags(&name, &value, &flags);
6617 if (res != ERROR_SUCCESS || !value)
6618 goto done;
6620 if (value && !deformat_string(package, value, &deformatted))
6622 res = ERROR_OUTOFMEMORY;
6623 goto done;
6626 value = deformatted;
6628 res = open_env_key( flags, &env );
6629 if (res != ERROR_SUCCESS)
6630 goto done;
6632 if (flags & ENV_MOD_MACHINE)
6633 action |= 0x20000000;
6635 size = 0;
6636 type = REG_SZ;
6637 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6638 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6639 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6640 goto done;
6642 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6644 action = 0x2;
6646 /* Nothing to do. */
6647 if (!value)
6649 res = ERROR_SUCCESS;
6650 goto done;
6653 /* If we are appending but the string was empty, strip ; */
6654 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6656 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6657 newval = strdupW(value);
6658 if (!newval)
6660 res = ERROR_OUTOFMEMORY;
6661 goto done;
6664 else
6666 action = 0x1;
6668 /* Contrary to MSDN, +-variable to [~];path works */
6669 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6671 res = ERROR_SUCCESS;
6672 goto done;
6675 data = msi_alloc(size);
6676 if (!data)
6678 RegCloseKey(env);
6679 return ERROR_OUTOFMEMORY;
6682 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6683 if (res != ERROR_SUCCESS)
6684 goto done;
6686 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6688 action = 0x4;
6689 res = RegDeleteValueW(env, name);
6690 if (res != ERROR_SUCCESS)
6691 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6692 goto done;
6695 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6696 if (flags & ENV_MOD_MASK)
6698 DWORD mod_size;
6699 int multiplier = 0;
6700 if (flags & ENV_MOD_APPEND) multiplier++;
6701 if (flags & ENV_MOD_PREFIX) multiplier++;
6702 mod_size = lstrlenW(value) * multiplier;
6703 size += mod_size * sizeof(WCHAR);
6706 newval = msi_alloc(size);
6707 ptr = newval;
6708 if (!newval)
6710 res = ERROR_OUTOFMEMORY;
6711 goto done;
6714 if (flags & ENV_MOD_PREFIX)
6716 lstrcpyW(newval, value);
6717 ptr = newval + lstrlenW(value);
6718 action |= 0x80000000;
6721 lstrcpyW(ptr, data);
6723 if (flags & ENV_MOD_APPEND)
6725 lstrcatW(newval, value);
6726 action |= 0x40000000;
6729 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6730 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6731 if (res)
6733 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6736 done:
6737 uirow = MSI_CreateRecord( 3 );
6738 MSI_RecordSetStringW( uirow, 1, name );
6739 MSI_RecordSetStringW( uirow, 2, newval );
6740 MSI_RecordSetInteger( uirow, 3, action );
6741 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6742 msiobj_release( &uirow->hdr );
6744 if (env) RegCloseKey(env);
6745 msi_free(deformatted);
6746 msi_free(data);
6747 msi_free(newval);
6748 return res;
6751 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6753 static const WCHAR query[] = {
6754 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6755 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6756 MSIQUERY *view;
6757 UINT rc;
6759 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6760 if (rc != ERROR_SUCCESS)
6761 return ERROR_SUCCESS;
6763 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6764 msiobj_release(&view->hdr);
6765 return rc;
6768 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6770 MSIPACKAGE *package = param;
6771 LPCWSTR name, value, component;
6772 LPWSTR deformatted = NULL;
6773 DWORD flags;
6774 HKEY env;
6775 MSICOMPONENT *comp;
6776 MSIRECORD *uirow;
6777 int action = 0;
6778 LONG res;
6779 UINT r;
6781 component = MSI_RecordGetString( rec, 4 );
6782 comp = msi_get_loaded_component( package, component );
6783 if (!comp)
6784 return ERROR_SUCCESS;
6786 comp->Action = msi_get_component_action( package, comp );
6787 if (comp->Action != INSTALLSTATE_ABSENT)
6789 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6790 return ERROR_SUCCESS;
6792 name = MSI_RecordGetString( rec, 2 );
6793 value = MSI_RecordGetString( rec, 3 );
6795 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6797 r = env_parse_flags( &name, &value, &flags );
6798 if (r != ERROR_SUCCESS)
6799 return r;
6801 if (!(flags & ENV_ACT_REMOVE))
6803 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6804 return ERROR_SUCCESS;
6807 if (value && !deformat_string( package, value, &deformatted ))
6808 return ERROR_OUTOFMEMORY;
6810 value = deformatted;
6812 r = open_env_key( flags, &env );
6813 if (r != ERROR_SUCCESS)
6815 r = ERROR_SUCCESS;
6816 goto done;
6819 if (flags & ENV_MOD_MACHINE)
6820 action |= 0x20000000;
6822 TRACE("Removing %s\n", debugstr_w(name));
6824 res = RegDeleteValueW( env, name );
6825 if (res != ERROR_SUCCESS)
6827 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6828 r = ERROR_SUCCESS;
6831 done:
6832 uirow = MSI_CreateRecord( 3 );
6833 MSI_RecordSetStringW( uirow, 1, name );
6834 MSI_RecordSetStringW( uirow, 2, value );
6835 MSI_RecordSetInteger( uirow, 3, action );
6836 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6837 msiobj_release( &uirow->hdr );
6839 if (env) RegCloseKey( env );
6840 msi_free( deformatted );
6841 return r;
6844 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6846 static const WCHAR query[] = {
6847 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6848 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6849 MSIQUERY *view;
6850 UINT rc;
6852 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6853 if (rc != ERROR_SUCCESS)
6854 return ERROR_SUCCESS;
6856 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6857 msiobj_release( &view->hdr );
6858 return rc;
6861 UINT msi_validate_product_id( MSIPACKAGE *package )
6863 LPWSTR key, template, id;
6864 UINT r = ERROR_SUCCESS;
6866 id = msi_dup_property( package->db, szProductID );
6867 if (id)
6869 msi_free( id );
6870 return ERROR_SUCCESS;
6872 template = msi_dup_property( package->db, szPIDTemplate );
6873 key = msi_dup_property( package->db, szPIDKEY );
6874 if (key && template)
6876 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6877 r = msi_set_property( package->db, szProductID, key );
6879 msi_free( template );
6880 msi_free( key );
6881 return r;
6884 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6886 return msi_validate_product_id( package );
6889 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6891 TRACE("\n");
6892 package->need_reboot_at_end = 1;
6893 return ERROR_SUCCESS;
6896 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6898 static const WCHAR szAvailableFreeReg[] =
6899 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6900 MSIRECORD *uirow;
6901 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6903 TRACE("%p %d kilobytes\n", package, space);
6905 uirow = MSI_CreateRecord( 1 );
6906 MSI_RecordSetInteger( uirow, 1, space );
6907 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6908 msiobj_release( &uirow->hdr );
6910 return ERROR_SUCCESS;
6913 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
6915 TRACE("%p\n", package);
6917 msi_set_property( package->db, szRollbackDisabled, szOne );
6918 return ERROR_SUCCESS;
6921 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
6923 FIXME("%p\n", package);
6924 return ERROR_SUCCESS;
6927 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
6929 static const WCHAR driver_query[] = {
6930 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6931 'O','D','B','C','D','r','i','v','e','r',0};
6932 static const WCHAR translator_query[] = {
6933 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6934 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6935 MSIQUERY *view;
6936 UINT r, count;
6938 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6939 if (r == ERROR_SUCCESS)
6941 count = 0;
6942 r = MSI_IterateRecords( view, &count, NULL, package );
6943 msiobj_release( &view->hdr );
6944 if (r != ERROR_SUCCESS)
6945 return r;
6946 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
6948 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6949 if (r == ERROR_SUCCESS)
6951 count = 0;
6952 r = MSI_IterateRecords( view, &count, NULL, package );
6953 msiobj_release( &view->hdr );
6954 if (r != ERROR_SUCCESS)
6955 return r;
6956 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
6958 return ERROR_SUCCESS;
6961 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
6963 MSIPACKAGE *package = param;
6964 const WCHAR *property = MSI_RecordGetString( rec, 1 );
6965 WCHAR *value;
6967 if ((value = msi_dup_property( package->db, property )))
6969 FIXME("remove %s\n", debugstr_w(value));
6970 msi_free( value );
6972 return ERROR_SUCCESS;
6975 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6977 static const WCHAR query[] = {
6978 'S','E','L','E','C','T',' ','A','c','t','i','o','n','P','r','o','p','e','r','t','y',' ',
6979 'F','R','O','M',' ','U','p','g','r','a','d','e',0};
6980 MSIQUERY *view;
6981 UINT r;
6983 r = MSI_DatabaseOpenViewW( package->db, query, &view );
6984 if (r == ERROR_SUCCESS)
6986 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
6987 msiobj_release( &view->hdr );
6988 if (r != ERROR_SUCCESS)
6989 return r;
6991 return ERROR_SUCCESS;
6994 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
6996 MSIPACKAGE *package = param;
6997 int attributes = MSI_RecordGetInteger( rec, 5 );
6999 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7001 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7002 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7003 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7004 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7005 HKEY hkey;
7006 UINT r;
7008 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7010 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7011 if (r != ERROR_SUCCESS)
7012 return ERROR_SUCCESS;
7014 else
7016 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7017 if (r != ERROR_SUCCESS)
7018 return ERROR_SUCCESS;
7020 RegCloseKey( hkey );
7022 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7023 debugstr_w(upgrade_code), debugstr_w(version_min),
7024 debugstr_w(version_max), debugstr_w(language));
7026 return ERROR_SUCCESS;
7029 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7031 static const WCHAR query[] = {
7032 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7033 'U','p','g','r','a','d','e',0};
7034 MSIQUERY *view;
7035 UINT r;
7037 if (msi_get_property_int( package->db, szInstalled, 0 ))
7039 TRACE("product is installed, skipping action\n");
7040 return ERROR_SUCCESS;
7042 if (msi_get_property_int( package->db, szPreselected, 0 ))
7044 TRACE("Preselected property is set, not migrating feature states\n");
7045 return ERROR_SUCCESS;
7047 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7048 if (r == ERROR_SUCCESS)
7050 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7051 msiobj_release( &view->hdr );
7052 if (r != ERROR_SUCCESS)
7053 return r;
7055 return ERROR_SUCCESS;
7058 static void bind_image( const char *filename, const char *path )
7060 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7062 WARN("failed to bind image %u\n", GetLastError());
7066 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7068 UINT i;
7069 MSIFILE *file;
7070 MSIPACKAGE *package = param;
7071 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7072 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7073 char *filenameA, *pathA;
7074 WCHAR *pathW, **path_list;
7076 if (!(file = msi_get_loaded_file( package, key )))
7078 WARN("file %s not found\n", debugstr_w(key));
7079 return ERROR_SUCCESS;
7081 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7082 path_list = msi_split_string( paths, ';' );
7083 if (!path_list) bind_image( filenameA, NULL );
7084 else
7086 for (i = 0; path_list[i] && path_list[i][0]; i++)
7088 deformat_string( package, path_list[i], &pathW );
7089 if ((pathA = strdupWtoA( pathW )))
7091 bind_image( filenameA, pathA );
7092 msi_free( pathA );
7094 msi_free( pathW );
7097 msi_free( path_list );
7098 msi_free( filenameA );
7099 return ERROR_SUCCESS;
7102 static UINT ACTION_BindImage( MSIPACKAGE *package )
7104 static const WCHAR query[] = {
7105 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7106 'B','i','n','d','I','m','a','g','e',0};
7107 MSIQUERY *view;
7108 UINT r;
7110 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7111 if (r == ERROR_SUCCESS)
7113 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7114 msiobj_release( &view->hdr );
7115 if (r != ERROR_SUCCESS)
7116 return r;
7118 return ERROR_SUCCESS;
7121 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7123 static const WCHAR query[] = {
7124 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7125 MSIQUERY *view;
7126 DWORD count = 0;
7127 UINT r;
7129 r = MSI_OpenQuery( package->db, &view, query, table );
7130 if (r == ERROR_SUCCESS)
7132 r = MSI_IterateRecords(view, &count, NULL, package);
7133 msiobj_release(&view->hdr);
7134 if (r != ERROR_SUCCESS)
7135 return r;
7137 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7138 return ERROR_SUCCESS;
7141 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7143 static const WCHAR table[] = {
7144 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7145 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7148 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7150 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7151 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7154 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7156 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7157 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7160 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7162 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7163 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7166 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7168 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7169 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7172 static const struct
7174 const WCHAR *action;
7175 UINT (*handler)(MSIPACKAGE *);
7176 const WCHAR *action_rollback;
7178 StandardActions[] =
7180 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7181 { szAppSearch, ACTION_AppSearch, NULL },
7182 { szBindImage, ACTION_BindImage, NULL },
7183 { szCCPSearch, ACTION_CCPSearch, NULL },
7184 { szCostFinalize, ACTION_CostFinalize, NULL },
7185 { szCostInitialize, ACTION_CostInitialize, NULL },
7186 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7187 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7188 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7189 { szDisableRollback, ACTION_DisableRollback, NULL },
7190 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7191 { szExecuteAction, ACTION_ExecuteAction, NULL },
7192 { szFileCost, ACTION_FileCost, NULL },
7193 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7194 { szForceReboot, ACTION_ForceReboot, NULL },
7195 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7196 { szInstallExecute, ACTION_InstallExecute, NULL },
7197 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7198 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7199 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7200 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7201 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7202 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7203 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7204 { szInstallValidate, ACTION_InstallValidate, NULL },
7205 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7206 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7207 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7208 { szMoveFiles, ACTION_MoveFiles, NULL },
7209 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7210 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7211 { szPatchFiles, ACTION_PatchFiles, NULL },
7212 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7213 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7214 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7215 { szPublishProduct, ACTION_PublishProduct, NULL },
7216 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7217 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7218 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7219 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7220 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7221 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7222 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7223 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7224 { szRegisterUser, ACTION_RegisterUser, NULL },
7225 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7226 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7227 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7228 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7229 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7230 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7231 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7232 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7233 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7234 { szResolveSource, ACTION_ResolveSource, NULL },
7235 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7236 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7237 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7238 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7239 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7240 { szStartServices, ACTION_StartServices, szStopServices },
7241 { szStopServices, ACTION_StopServices, szStartServices },
7242 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7243 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7244 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7245 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7246 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7247 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7248 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7249 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7250 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7251 { szValidateProductID, ACTION_ValidateProductID, NULL },
7252 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7253 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7254 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7255 { NULL, NULL, NULL }
7258 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7260 BOOL ret = FALSE;
7261 UINT i;
7263 i = 0;
7264 while (StandardActions[i].action != NULL)
7266 if (!strcmpW( StandardActions[i].action, action ))
7268 ui_actionstart( package, action );
7269 if (StandardActions[i].handler)
7271 ui_actioninfo( package, action, TRUE, 0 );
7272 *rc = StandardActions[i].handler( package );
7273 ui_actioninfo( package, action, FALSE, *rc );
7275 if (StandardActions[i].action_rollback && !package->need_rollback)
7277 TRACE("scheduling rollback action\n");
7278 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7281 else
7283 FIXME("unhandled standard action %s\n", debugstr_w(action));
7284 *rc = ERROR_SUCCESS;
7286 ret = TRUE;
7287 break;
7289 i++;
7291 return ret;
7294 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7296 UINT rc = ERROR_SUCCESS;
7297 BOOL handled;
7299 TRACE("Performing action (%s)\n", debugstr_w(action));
7301 handled = ACTION_HandleStandardAction(package, action, &rc);
7303 if (!handled)
7304 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7306 if (!handled)
7308 WARN("unhandled msi action %s\n", debugstr_w(action));
7309 rc = ERROR_FUNCTION_NOT_CALLED;
7312 return rc;
7315 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7317 UINT rc = ERROR_SUCCESS;
7318 BOOL handled = FALSE;
7320 TRACE("Performing action (%s)\n", debugstr_w(action));
7322 handled = ACTION_HandleStandardAction(package, action, &rc);
7324 if (!handled)
7325 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7327 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7328 handled = TRUE;
7330 if (!handled)
7332 WARN("unhandled msi action %s\n", debugstr_w(action));
7333 rc = ERROR_FUNCTION_NOT_CALLED;
7336 return rc;
7339 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7341 UINT rc = ERROR_SUCCESS;
7342 MSIRECORD *row;
7344 static const WCHAR query[] =
7345 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7346 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7347 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7348 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7349 static const WCHAR ui_query[] =
7350 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7351 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7352 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7353 ' ', '=',' ','%','i',0};
7355 if (needs_ui_sequence(package))
7356 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7357 else
7358 row = MSI_QueryGetRecord(package->db, query, seq);
7360 if (row)
7362 LPCWSTR action, cond;
7364 TRACE("Running the actions\n");
7366 /* check conditions */
7367 cond = MSI_RecordGetString(row, 2);
7369 /* this is a hack to skip errors in the condition code */
7370 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7372 msiobj_release(&row->hdr);
7373 return ERROR_SUCCESS;
7376 action = MSI_RecordGetString(row, 1);
7377 if (!action)
7379 ERR("failed to fetch action\n");
7380 msiobj_release(&row->hdr);
7381 return ERROR_FUNCTION_FAILED;
7384 if (needs_ui_sequence(package))
7385 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7386 else
7387 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7389 msiobj_release(&row->hdr);
7392 return rc;
7395 /****************************************************
7396 * TOP level entry points
7397 *****************************************************/
7399 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7400 LPCWSTR szCommandLine )
7402 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7403 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7404 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7405 WCHAR *reinstall = NULL;
7406 BOOL ui_exists;
7407 UINT rc;
7409 msi_set_property( package->db, szAction, szInstall );
7411 package->script->InWhatSequence = SEQUENCE_INSTALL;
7413 if (szPackagePath)
7415 LPWSTR p, dir;
7416 LPCWSTR file;
7418 dir = strdupW(szPackagePath);
7419 p = strrchrW(dir, '\\');
7420 if (p)
7422 *(++p) = 0;
7423 file = szPackagePath + (p - dir);
7425 else
7427 msi_free(dir);
7428 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7429 GetCurrentDirectoryW(MAX_PATH, dir);
7430 lstrcatW(dir, szBackSlash);
7431 file = szPackagePath;
7434 msi_free( package->PackagePath );
7435 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7436 if (!package->PackagePath)
7438 msi_free(dir);
7439 return ERROR_OUTOFMEMORY;
7442 lstrcpyW(package->PackagePath, dir);
7443 lstrcatW(package->PackagePath, file);
7444 msi_free(dir);
7446 msi_set_sourcedir_props(package, FALSE);
7449 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7450 if (rc != ERROR_SUCCESS)
7451 return rc;
7453 msi_apply_transforms( package );
7454 msi_apply_patches( package );
7456 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7458 TRACE("setting reinstall property\n");
7459 msi_set_property( package->db, szReinstall, szAll );
7462 /* properties may have been added by a transform */
7463 msi_clone_properties( package );
7465 msi_parse_command_line( package, szCommandLine, FALSE );
7466 msi_adjust_privilege_properties( package );
7467 msi_set_context( package );
7469 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7471 TRACE("disabling rollback\n");
7472 msi_set_property( package->db, szRollbackDisabled, szOne );
7475 if (needs_ui_sequence( package))
7477 package->script->InWhatSequence |= SEQUENCE_UI;
7478 rc = ACTION_ProcessUISequence(package);
7479 ui_exists = ui_sequence_exists(package);
7480 if (rc == ERROR_SUCCESS || !ui_exists)
7482 package->script->InWhatSequence |= SEQUENCE_EXEC;
7483 rc = ACTION_ProcessExecSequence(package, ui_exists);
7486 else
7487 rc = ACTION_ProcessExecSequence(package, FALSE);
7489 package->script->CurrentlyScripting = FALSE;
7491 /* process the ending type action */
7492 if (rc == ERROR_SUCCESS)
7493 ACTION_PerformActionSequence(package, -1);
7494 else if (rc == ERROR_INSTALL_USEREXIT)
7495 ACTION_PerformActionSequence(package, -2);
7496 else if (rc == ERROR_INSTALL_SUSPEND)
7497 ACTION_PerformActionSequence(package, -4);
7498 else /* failed */
7500 ACTION_PerformActionSequence(package, -3);
7501 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7503 package->need_rollback = TRUE;
7507 /* finish up running custom actions */
7508 ACTION_FinishCustomActions(package);
7510 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7512 WARN("installation failed, running rollback script\n");
7513 execute_script( package, SCRIPT_ROLLBACK );
7515 msi_free( reinstall );
7517 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7518 return ERROR_SUCCESS_REBOOT_REQUIRED;
7520 return rc;