msi: Implement the RemoveExistingProducts standard action.
[wine/multimedia.git] / dlls / msi / action.c
blob1ad3bc2b26e5c10c38c2ccefac40e777b560526a
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 in_quotes = 1;
226 ignore = 1;
227 len++;
228 break;
229 case '"':
230 state = state_quote;
231 if (in_quotes && p[1] != '\"') count--;
232 else count++;
233 break;
234 default:
235 state = state_token;
236 in_quotes = 1;
237 len++;
238 break;
240 break;
242 case state_token:
243 switch (*p)
245 case '"':
246 state = state_quote;
247 if (in_quotes) count--;
248 else count++;
249 break;
250 case ' ':
251 state = state_whitespace;
252 if (!count) goto done;
253 in_quotes = 1;
254 len++;
255 break;
256 default:
257 if (!count) in_quotes = 0;
258 else in_quotes = 1;
259 len++;
260 break;
262 break;
264 case state_quote:
265 switch (*p)
267 case '"':
268 if (in_quotes && p[1] != '\"') count--;
269 else count++;
270 break;
271 case ' ':
272 state = state_whitespace;
273 if (!count || (count > 1 && !len)) goto done;
274 in_quotes = 1;
275 len++;
276 break;
277 default:
278 state = state_token;
279 if (!count) in_quotes = 0;
280 else in_quotes = 1;
281 len++;
282 break;
284 break;
286 default: break;
288 if (!ignore) *out++ = *p;
291 done:
292 if (!len) *value = 0;
293 else *out = 0;
295 *quotes = count;
296 return p - str;
299 static void remove_quotes( WCHAR *str )
301 WCHAR *p = str;
302 int len = strlenW( str );
304 while ((p = strchrW( p, '"' )))
306 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
307 p++;
311 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
312 BOOL preserve_case )
314 LPCWSTR ptr, ptr2;
315 int num_quotes;
316 DWORD len;
317 WCHAR *prop, *val;
318 UINT r;
320 if (!szCommandLine)
321 return ERROR_SUCCESS;
323 ptr = szCommandLine;
324 while (*ptr)
326 while (*ptr == ' ') ptr++;
327 if (!*ptr) break;
329 ptr2 = strchrW( ptr, '=' );
330 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
332 len = ptr2 - ptr;
333 if (!len) return ERROR_INVALID_COMMAND_LINE;
335 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
336 memcpy( prop, ptr, len * sizeof(WCHAR) );
337 prop[len] = 0;
338 if (!preserve_case) struprW( prop );
340 ptr2++;
341 while (*ptr2 == ' ') ptr2++;
343 num_quotes = 0;
344 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
345 len = parse_prop( ptr2, val, &num_quotes );
346 if (num_quotes % 2)
348 WARN("unbalanced quotes\n");
349 msi_free( val );
350 msi_free( prop );
351 return ERROR_INVALID_COMMAND_LINE;
353 remove_quotes( val );
354 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
356 r = msi_set_property( package->db, prop, val );
357 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
358 msi_reset_folders( package, TRUE );
360 msi_free( val );
361 msi_free( prop );
363 ptr = ptr2 + len;
366 return ERROR_SUCCESS;
369 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
371 LPCWSTR pc;
372 LPWSTR p, *ret = NULL;
373 UINT count = 0;
375 if (!str)
376 return ret;
378 /* count the number of substrings */
379 for ( pc = str, count = 0; pc; count++ )
381 pc = strchrW( pc, sep );
382 if (pc)
383 pc++;
386 /* allocate space for an array of substring pointers and the substrings */
387 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
388 (lstrlenW(str)+1) * sizeof(WCHAR) );
389 if (!ret)
390 return ret;
392 /* copy the string and set the pointers */
393 p = (LPWSTR) &ret[count+1];
394 lstrcpyW( p, str );
395 for( count = 0; (ret[count] = p); count++ )
397 p = strchrW( p, sep );
398 if (p)
399 *p++ = 0;
402 return ret;
405 static BOOL ui_sequence_exists( MSIPACKAGE *package )
407 static const WCHAR query [] = {
408 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
409 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
410 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
411 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
412 MSIQUERY *view;
413 UINT rc;
415 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
416 if (rc == ERROR_SUCCESS)
418 msiobj_release(&view->hdr);
419 return TRUE;
421 return FALSE;
424 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
426 LPWSTR source, check;
428 if (msi_get_property_int( package->db, szInstalled, 0 ))
430 HKEY hkey;
432 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
433 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
434 RegCloseKey( hkey );
436 else
438 LPWSTR p, db;
439 DWORD len;
441 db = msi_dup_property( package->db, szOriginalDatabase );
442 if (!db)
443 return ERROR_OUTOFMEMORY;
445 p = strrchrW( db, '\\' );
446 if (!p)
448 p = strrchrW( db, '/' );
449 if (!p)
451 msi_free(db);
452 return ERROR_SUCCESS;
456 len = p - db + 2;
457 source = msi_alloc( len * sizeof(WCHAR) );
458 lstrcpynW( source, db, len );
459 msi_free( db );
462 check = msi_dup_property( package->db, szSourceDir );
463 if (!check || replace)
465 UINT r = msi_set_property( package->db, szSourceDir, source );
466 if (r == ERROR_SUCCESS)
467 msi_reset_folders( package, TRUE );
469 msi_free( check );
471 check = msi_dup_property( package->db, szSOURCEDIR );
472 if (!check || replace)
473 msi_set_property( package->db, szSOURCEDIR, source );
475 msi_free( check );
476 msi_free( source );
478 return ERROR_SUCCESS;
481 static BOOL needs_ui_sequence(MSIPACKAGE *package)
483 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
486 UINT msi_set_context(MSIPACKAGE *package)
488 UINT r = msi_locate_product( package->ProductCode, &package->Context );
489 if (r != ERROR_SUCCESS)
491 int num = msi_get_property_int( package->db, szAllUsers, 0 );
492 if (num == 1 || num == 2)
493 package->Context = MSIINSTALLCONTEXT_MACHINE;
494 else
495 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
497 return ERROR_SUCCESS;
500 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
502 UINT rc;
503 LPCWSTR cond, action;
504 MSIPACKAGE *package = param;
506 action = MSI_RecordGetString(row,1);
507 if (!action)
509 ERR("Error is retrieving action name\n");
510 return ERROR_FUNCTION_FAILED;
513 /* check conditions */
514 cond = MSI_RecordGetString(row,2);
516 /* this is a hack to skip errors in the condition code */
517 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
519 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
520 return ERROR_SUCCESS;
523 if (needs_ui_sequence(package))
524 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
525 else
526 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
528 msi_dialog_check_messages( NULL );
530 if (package->CurrentInstallState != ERROR_SUCCESS)
531 rc = package->CurrentInstallState;
533 if (rc == ERROR_FUNCTION_NOT_CALLED)
534 rc = ERROR_SUCCESS;
536 if (rc != ERROR_SUCCESS)
537 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
539 if (package->need_reboot_now)
541 TRACE("action %s asked for immediate reboot, suspending installation\n",
542 debugstr_w(action));
543 rc = ACTION_ForceReboot( package );
545 return rc;
548 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
550 static const WCHAR query[] = {
551 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
552 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
553 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
554 '`','S','e','q','u','e','n','c','e','`',0};
555 MSIQUERY *view;
556 UINT r;
558 TRACE("%p %s\n", package, debugstr_w(table));
560 r = MSI_OpenQuery( package->db, &view, query, table );
561 if (r == ERROR_SUCCESS)
563 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
564 msiobj_release(&view->hdr);
566 return r;
569 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
571 static const WCHAR query[] = {
572 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
573 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
574 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
575 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
576 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
577 static const WCHAR query_validate[] = {
578 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
579 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
580 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
581 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
582 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
583 MSIQUERY *view;
584 INT seq = 0;
585 UINT rc;
587 if (package->script->ExecuteSequenceRun)
589 TRACE("Execute Sequence already Run\n");
590 return ERROR_SUCCESS;
593 package->script->ExecuteSequenceRun = TRUE;
595 /* get the sequence number */
596 if (UIran)
598 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
599 if (!row) return ERROR_FUNCTION_FAILED;
600 seq = MSI_RecordGetInteger(row,1);
601 msiobj_release(&row->hdr);
603 rc = MSI_OpenQuery(package->db, &view, query, seq);
604 if (rc == ERROR_SUCCESS)
606 TRACE("Running the actions\n");
608 msi_set_property(package->db, szSourceDir, NULL);
609 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
610 msiobj_release(&view->hdr);
612 return rc;
615 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
617 static const WCHAR query[] = {
618 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
619 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
620 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
621 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
622 MSIQUERY *view;
623 UINT rc;
625 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
626 if (rc == ERROR_SUCCESS)
628 TRACE("Running the actions\n");
629 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
630 msiobj_release(&view->hdr);
632 return rc;
635 /********************************************************
636 * ACTION helper functions and functions that perform the actions
637 *******************************************************/
638 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
639 UINT* rc, UINT script, BOOL force )
641 BOOL ret=FALSE;
642 UINT arc;
644 arc = ACTION_CustomAction(package, action, script, force);
646 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
648 *rc = arc;
649 ret = TRUE;
651 return ret;
654 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
656 MSICOMPONENT *comp;
658 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
660 if (!strcmpW( Component, comp->Component )) return comp;
662 return NULL;
665 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
667 MSIFEATURE *feature;
669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
671 if (!strcmpW( Feature, feature->Feature )) return feature;
673 return NULL;
676 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
678 MSIFILE *file;
680 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
682 if (!strcmpW( key, file->File )) return file;
684 return NULL;
687 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
689 MSIFILEPATCH *patch;
691 /* FIXME: There might be more than one patch */
692 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
694 if (!strcmpW( key, patch->File->File )) return patch;
696 return NULL;
699 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
701 MSIFOLDER *folder;
703 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
705 if (!strcmpW( dir, folder->Directory )) return folder;
707 return NULL;
711 * Recursively create all directories in the path.
712 * shamelessly stolen from setupapi/queue.c
714 BOOL msi_create_full_path( const WCHAR *path )
716 BOOL ret = TRUE;
717 WCHAR *new_path;
718 int len;
720 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
721 strcpyW( new_path, path );
723 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
724 new_path[len - 1] = 0;
726 while (!CreateDirectoryW( new_path, NULL ))
728 WCHAR *slash;
729 DWORD last_error = GetLastError();
730 if (last_error == ERROR_ALREADY_EXISTS) break;
731 if (last_error != ERROR_PATH_NOT_FOUND)
733 ret = FALSE;
734 break;
736 if (!(slash = strrchrW( new_path, '\\' )))
738 ret = FALSE;
739 break;
741 len = slash - new_path;
742 new_path[len] = 0;
743 if (!msi_create_full_path( new_path ))
745 ret = FALSE;
746 break;
748 new_path[len] = '\\';
750 msi_free( new_path );
751 return ret;
754 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
756 MSIRECORD *row;
758 row = MSI_CreateRecord( 4 );
759 MSI_RecordSetInteger( row, 1, a );
760 MSI_RecordSetInteger( row, 2, b );
761 MSI_RecordSetInteger( row, 3, c );
762 MSI_RecordSetInteger( row, 4, d );
763 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
764 msiobj_release( &row->hdr );
766 msi_dialog_check_messages( NULL );
769 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
771 static const WCHAR query[] =
772 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
773 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
774 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
775 WCHAR message[1024];
776 MSIRECORD *row = 0;
777 DWORD size;
779 if (!package->LastAction || strcmpW( package->LastAction, action ))
781 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
783 if (MSI_RecordIsNull( row, 3 ))
785 msiobj_release( &row->hdr );
786 return;
788 /* update the cached action format */
789 msi_free( package->ActionFormat );
790 package->ActionFormat = msi_dup_record_field( row, 3 );
791 msi_free( package->LastAction );
792 package->LastAction = strdupW( action );
793 msiobj_release( &row->hdr );
795 size = 1024;
796 MSI_RecordSetStringW( record, 0, package->ActionFormat );
797 MSI_FormatRecordW( package, record, message, &size );
798 row = MSI_CreateRecord( 1 );
799 MSI_RecordSetStringW( row, 1, message );
800 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
801 msiobj_release( &row->hdr );
804 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
806 if (!comp->Enabled)
808 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
809 return INSTALLSTATE_UNKNOWN;
811 if (package->need_rollback) return comp->Installed;
812 return comp->ActionRequest;
815 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
817 if (package->need_rollback) return feature->Installed;
818 return feature->ActionRequest;
821 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
823 MSIPACKAGE *package = param;
824 LPCWSTR dir, component, full_path;
825 MSIRECORD *uirow;
826 MSIFOLDER *folder;
827 MSICOMPONENT *comp;
829 component = MSI_RecordGetString(row, 2);
830 if (!component)
831 return ERROR_SUCCESS;
833 comp = msi_get_loaded_component(package, component);
834 if (!comp)
835 return ERROR_SUCCESS;
837 comp->Action = msi_get_component_action( package, comp );
838 if (comp->Action != INSTALLSTATE_LOCAL)
840 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
841 return ERROR_SUCCESS;
844 dir = MSI_RecordGetString(row,1);
845 if (!dir)
847 ERR("Unable to get folder id\n");
848 return ERROR_SUCCESS;
851 uirow = MSI_CreateRecord(1);
852 MSI_RecordSetStringW(uirow, 1, dir);
853 msi_ui_actiondata(package, szCreateFolders, uirow);
854 msiobj_release(&uirow->hdr);
856 full_path = msi_get_target_folder( package, dir );
857 if (!full_path)
859 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
860 return ERROR_SUCCESS;
862 TRACE("folder is %s\n", debugstr_w(full_path));
864 folder = msi_get_loaded_folder( package, dir );
865 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
866 folder->State = FOLDER_STATE_CREATED;
867 return ERROR_SUCCESS;
870 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
872 static const WCHAR query[] = {
873 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
874 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
875 MSIQUERY *view;
876 UINT rc;
878 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
879 if (rc != ERROR_SUCCESS)
880 return ERROR_SUCCESS;
882 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
883 msiobj_release(&view->hdr);
884 return rc;
887 static void remove_persistent_folder( MSIFOLDER *folder )
889 FolderList *fl;
891 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
893 remove_persistent_folder( fl->folder );
895 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
897 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
901 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
903 MSIPACKAGE *package = param;
904 LPCWSTR dir, component, full_path;
905 MSIRECORD *uirow;
906 MSIFOLDER *folder;
907 MSICOMPONENT *comp;
909 component = MSI_RecordGetString(row, 2);
910 if (!component)
911 return ERROR_SUCCESS;
913 comp = msi_get_loaded_component(package, component);
914 if (!comp)
915 return ERROR_SUCCESS;
917 comp->Action = msi_get_component_action( package, comp );
918 if (comp->Action != INSTALLSTATE_ABSENT)
920 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
921 return ERROR_SUCCESS;
924 dir = MSI_RecordGetString( row, 1 );
925 if (!dir)
927 ERR("Unable to get folder id\n");
928 return ERROR_SUCCESS;
931 full_path = msi_get_target_folder( package, dir );
932 if (!full_path)
934 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
935 return ERROR_SUCCESS;
937 TRACE("folder is %s\n", debugstr_w(full_path));
939 uirow = MSI_CreateRecord( 1 );
940 MSI_RecordSetStringW( uirow, 1, dir );
941 msi_ui_actiondata( package, szRemoveFolders, uirow );
942 msiobj_release( &uirow->hdr );
944 folder = msi_get_loaded_folder( package, dir );
945 remove_persistent_folder( folder );
946 return ERROR_SUCCESS;
949 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
951 static const WCHAR query[] = {
952 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
953 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
954 MSIQUERY *view;
955 UINT rc;
957 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
958 if (rc != ERROR_SUCCESS)
959 return ERROR_SUCCESS;
961 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
962 msiobj_release( &view->hdr );
963 return rc;
966 static UINT load_component( MSIRECORD *row, LPVOID param )
968 MSIPACKAGE *package = param;
969 MSICOMPONENT *comp;
971 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
972 if (!comp)
973 return ERROR_FUNCTION_FAILED;
975 list_add_tail( &package->components, &comp->entry );
977 /* fill in the data */
978 comp->Component = msi_dup_record_field( row, 1 );
980 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
982 comp->ComponentId = msi_dup_record_field( row, 2 );
983 comp->Directory = msi_dup_record_field( row, 3 );
984 comp->Attributes = MSI_RecordGetInteger(row,4);
985 comp->Condition = msi_dup_record_field( row, 5 );
986 comp->KeyPath = msi_dup_record_field( row, 6 );
988 comp->Installed = INSTALLSTATE_UNKNOWN;
989 comp->Action = INSTALLSTATE_UNKNOWN;
990 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
992 comp->assembly = msi_load_assembly( package, comp );
993 return ERROR_SUCCESS;
996 UINT msi_load_all_components( MSIPACKAGE *package )
998 static const WCHAR query[] = {
999 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1000 '`','C','o','m','p','o','n','e','n','t','`',0};
1001 MSIQUERY *view;
1002 UINT r;
1004 if (!list_empty(&package->components))
1005 return ERROR_SUCCESS;
1007 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1008 if (r != ERROR_SUCCESS)
1009 return r;
1011 if (!msi_init_assembly_caches( package ))
1013 ERR("can't initialize assembly caches\n");
1014 msiobj_release( &view->hdr );
1015 return ERROR_FUNCTION_FAILED;
1018 r = MSI_IterateRecords(view, NULL, load_component, package);
1019 msiobj_release(&view->hdr);
1020 return r;
1023 typedef struct {
1024 MSIPACKAGE *package;
1025 MSIFEATURE *feature;
1026 } _ilfs;
1028 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1030 ComponentList *cl;
1032 cl = msi_alloc( sizeof (*cl) );
1033 if ( !cl )
1034 return ERROR_NOT_ENOUGH_MEMORY;
1035 cl->component = comp;
1036 list_add_tail( &feature->Components, &cl->entry );
1038 return ERROR_SUCCESS;
1041 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1043 FeatureList *fl;
1045 fl = msi_alloc( sizeof(*fl) );
1046 if ( !fl )
1047 return ERROR_NOT_ENOUGH_MEMORY;
1048 fl->feature = child;
1049 list_add_tail( &parent->Children, &fl->entry );
1051 return ERROR_SUCCESS;
1054 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1056 _ilfs* ilfs = param;
1057 LPCWSTR component;
1058 MSICOMPONENT *comp;
1060 component = MSI_RecordGetString(row,1);
1062 /* check to see if the component is already loaded */
1063 comp = msi_get_loaded_component( ilfs->package, component );
1064 if (!comp)
1066 WARN("ignoring unknown component %s\n", debugstr_w(component));
1067 return ERROR_SUCCESS;
1069 add_feature_component( ilfs->feature, comp );
1070 comp->Enabled = TRUE;
1072 return ERROR_SUCCESS;
1075 static UINT load_feature(MSIRECORD * row, LPVOID param)
1077 static const WCHAR query[] = {
1078 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1079 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1080 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1081 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1082 MSIPACKAGE *package = param;
1083 MSIFEATURE *feature;
1084 MSIQUERY *view;
1085 _ilfs ilfs;
1086 UINT rc;
1088 /* fill in the data */
1090 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1091 if (!feature)
1092 return ERROR_NOT_ENOUGH_MEMORY;
1094 list_init( &feature->Children );
1095 list_init( &feature->Components );
1097 feature->Feature = msi_dup_record_field( row, 1 );
1099 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1101 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1102 feature->Title = msi_dup_record_field( row, 3 );
1103 feature->Description = msi_dup_record_field( row, 4 );
1105 if (!MSI_RecordIsNull(row,5))
1106 feature->Display = MSI_RecordGetInteger(row,5);
1108 feature->Level= MSI_RecordGetInteger(row,6);
1109 feature->Directory = msi_dup_record_field( row, 7 );
1110 feature->Attributes = MSI_RecordGetInteger(row,8);
1112 feature->Installed = INSTALLSTATE_UNKNOWN;
1113 feature->Action = INSTALLSTATE_UNKNOWN;
1114 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1116 list_add_tail( &package->features, &feature->entry );
1118 /* load feature components */
1120 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1121 if (rc != ERROR_SUCCESS)
1122 return ERROR_SUCCESS;
1124 ilfs.package = package;
1125 ilfs.feature = feature;
1127 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1128 msiobj_release(&view->hdr);
1129 return rc;
1132 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1134 MSIPACKAGE *package = param;
1135 MSIFEATURE *parent, *child;
1137 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1138 if (!child)
1139 return ERROR_FUNCTION_FAILED;
1141 if (!child->Feature_Parent)
1142 return ERROR_SUCCESS;
1144 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1145 if (!parent)
1146 return ERROR_FUNCTION_FAILED;
1148 add_feature_child( parent, child );
1149 return ERROR_SUCCESS;
1152 UINT msi_load_all_features( MSIPACKAGE *package )
1154 static const WCHAR query[] = {
1155 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1156 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1157 '`','D','i','s','p','l','a','y','`',0};
1158 MSIQUERY *view;
1159 UINT r;
1161 if (!list_empty(&package->features))
1162 return ERROR_SUCCESS;
1164 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1165 if (r != ERROR_SUCCESS)
1166 return r;
1168 r = MSI_IterateRecords( view, NULL, load_feature, package );
1169 if (r != ERROR_SUCCESS)
1171 msiobj_release( &view->hdr );
1172 return r;
1174 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1175 msiobj_release( &view->hdr );
1176 return r;
1179 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1181 if (!p)
1182 return p;
1183 p = strchrW(p, ch);
1184 if (!p)
1185 return p;
1186 *p = 0;
1187 return p+1;
1190 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1192 static const WCHAR query[] = {
1193 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1194 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1195 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1196 MSIQUERY *view = NULL;
1197 MSIRECORD *row = NULL;
1198 UINT r;
1200 TRACE("%s\n", debugstr_w(file->File));
1202 r = MSI_OpenQuery(package->db, &view, query, file->File);
1203 if (r != ERROR_SUCCESS)
1204 goto done;
1206 r = MSI_ViewExecute(view, NULL);
1207 if (r != ERROR_SUCCESS)
1208 goto done;
1210 r = MSI_ViewFetch(view, &row);
1211 if (r != ERROR_SUCCESS)
1212 goto done;
1214 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1215 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1216 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1217 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1218 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1220 done:
1221 if (view) msiobj_release(&view->hdr);
1222 if (row) msiobj_release(&row->hdr);
1223 return r;
1226 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1228 MSIRECORD *row;
1229 static const WCHAR query[] = {
1230 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1231 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1232 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1234 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1235 if (!row)
1237 WARN("query failed\n");
1238 return ERROR_FUNCTION_FAILED;
1241 file->disk_id = MSI_RecordGetInteger( row, 1 );
1242 msiobj_release( &row->hdr );
1243 return ERROR_SUCCESS;
1246 static UINT load_file(MSIRECORD *row, LPVOID param)
1248 MSIPACKAGE* package = param;
1249 LPCWSTR component;
1250 MSIFILE *file;
1252 /* fill in the data */
1254 file = msi_alloc_zero( sizeof (MSIFILE) );
1255 if (!file)
1256 return ERROR_NOT_ENOUGH_MEMORY;
1258 file->File = msi_dup_record_field( row, 1 );
1260 component = MSI_RecordGetString( row, 2 );
1261 file->Component = msi_get_loaded_component( package, component );
1263 if (!file->Component)
1265 WARN("Component not found: %s\n", debugstr_w(component));
1266 msi_free(file->File);
1267 msi_free(file);
1268 return ERROR_SUCCESS;
1271 file->FileName = msi_dup_record_field( row, 3 );
1272 msi_reduce_to_long_filename( file->FileName );
1274 file->ShortName = msi_dup_record_field( row, 3 );
1275 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1277 file->FileSize = MSI_RecordGetInteger( row, 4 );
1278 file->Version = msi_dup_record_field( row, 5 );
1279 file->Language = msi_dup_record_field( row, 6 );
1280 file->Attributes = MSI_RecordGetInteger( row, 7 );
1281 file->Sequence = MSI_RecordGetInteger( row, 8 );
1283 file->state = msifs_invalid;
1285 /* if the compressed bits are not set in the file attributes,
1286 * then read the information from the package word count property
1288 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1290 file->IsCompressed = FALSE;
1292 else if (file->Attributes &
1293 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1295 file->IsCompressed = TRUE;
1297 else if (file->Attributes & msidbFileAttributesNoncompressed)
1299 file->IsCompressed = FALSE;
1301 else
1303 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1306 load_file_hash(package, file);
1307 load_file_disk_id(package, file);
1309 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1311 list_add_tail( &package->files, &file->entry );
1313 return ERROR_SUCCESS;
1316 static UINT load_all_files(MSIPACKAGE *package)
1318 static const WCHAR query[] = {
1319 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1320 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1321 '`','S','e','q','u','e','n','c','e','`', 0};
1322 MSIQUERY *view;
1323 UINT rc;
1325 if (!list_empty(&package->files))
1326 return ERROR_SUCCESS;
1328 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1329 if (rc != ERROR_SUCCESS)
1330 return ERROR_SUCCESS;
1332 rc = MSI_IterateRecords(view, NULL, load_file, package);
1333 msiobj_release(&view->hdr);
1334 return rc;
1337 static UINT load_media( MSIRECORD *row, LPVOID param )
1339 MSIPACKAGE *package = param;
1340 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1341 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1343 /* FIXME: load external cabinets and directory sources too */
1344 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1345 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1346 return ERROR_SUCCESS;
1349 static UINT load_all_media( MSIPACKAGE *package )
1351 static const WCHAR query[] = {
1352 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1353 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1354 '`','D','i','s','k','I','d','`',0};
1355 MSIQUERY *view;
1356 UINT r;
1358 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1359 if (r != ERROR_SUCCESS)
1360 return ERROR_SUCCESS;
1362 r = MSI_IterateRecords( view, NULL, load_media, package );
1363 msiobj_release( &view->hdr );
1364 return r;
1367 static UINT load_patch(MSIRECORD *row, LPVOID param)
1369 MSIPACKAGE *package = param;
1370 MSIFILEPATCH *patch;
1371 LPWSTR file_key;
1373 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1374 if (!patch)
1375 return ERROR_NOT_ENOUGH_MEMORY;
1377 file_key = msi_dup_record_field( row, 1 );
1378 patch->File = msi_get_loaded_file( package, file_key );
1379 msi_free(file_key);
1381 if( !patch->File )
1383 ERR("Failed to find target for patch in File table\n");
1384 msi_free(patch);
1385 return ERROR_FUNCTION_FAILED;
1388 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1390 /* FIXME: The database should be properly transformed */
1391 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1393 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1394 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1395 patch->IsApplied = FALSE;
1397 /* FIXME:
1398 * Header field - for patch validation.
1399 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1402 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1404 list_add_tail( &package->filepatches, &patch->entry );
1406 return ERROR_SUCCESS;
1409 static UINT load_all_patches(MSIPACKAGE *package)
1411 static const WCHAR query[] = {
1412 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1413 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1414 '`','S','e','q','u','e','n','c','e','`',0};
1415 MSIQUERY *view;
1416 UINT rc;
1418 if (!list_empty(&package->filepatches))
1419 return ERROR_SUCCESS;
1421 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1422 if (rc != ERROR_SUCCESS)
1423 return ERROR_SUCCESS;
1425 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1426 msiobj_release(&view->hdr);
1427 return rc;
1430 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1432 static const WCHAR query[] = {
1433 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1434 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1435 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1436 MSIQUERY *view;
1438 folder->persistent = FALSE;
1439 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1441 if (!MSI_ViewExecute( view, NULL ))
1443 MSIRECORD *rec;
1444 if (!MSI_ViewFetch( view, &rec ))
1446 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1447 folder->persistent = TRUE;
1448 msiobj_release( &rec->hdr );
1451 msiobj_release( &view->hdr );
1453 return ERROR_SUCCESS;
1456 static UINT load_folder( MSIRECORD *row, LPVOID param )
1458 MSIPACKAGE *package = param;
1459 static WCHAR szEmpty[] = { 0 };
1460 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1461 MSIFOLDER *folder;
1463 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1464 list_init( &folder->children );
1465 folder->Directory = msi_dup_record_field( row, 1 );
1466 folder->Parent = msi_dup_record_field( row, 2 );
1467 p = msi_dup_record_field(row, 3);
1469 TRACE("%s\n", debugstr_w(folder->Directory));
1471 /* split src and target dir */
1472 tgt_short = p;
1473 src_short = folder_split_path( p, ':' );
1475 /* split the long and short paths */
1476 tgt_long = folder_split_path( tgt_short, '|' );
1477 src_long = folder_split_path( src_short, '|' );
1479 /* check for no-op dirs */
1480 if (tgt_short && !strcmpW( szDot, tgt_short ))
1481 tgt_short = szEmpty;
1482 if (src_short && !strcmpW( szDot, src_short ))
1483 src_short = szEmpty;
1485 if (!tgt_long)
1486 tgt_long = tgt_short;
1488 if (!src_short) {
1489 src_short = tgt_short;
1490 src_long = tgt_long;
1493 if (!src_long)
1494 src_long = src_short;
1496 /* FIXME: use the target short path too */
1497 folder->TargetDefault = strdupW(tgt_long);
1498 folder->SourceShortPath = strdupW(src_short);
1499 folder->SourceLongPath = strdupW(src_long);
1500 msi_free(p);
1502 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1503 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1504 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1506 load_folder_persistence( package, folder );
1508 list_add_tail( &package->folders, &folder->entry );
1509 return ERROR_SUCCESS;
1512 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1514 FolderList *fl;
1516 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1517 fl->folder = child;
1518 list_add_tail( &parent->children, &fl->entry );
1519 return ERROR_SUCCESS;
1522 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1524 MSIPACKAGE *package = param;
1525 MSIFOLDER *parent, *child;
1527 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1528 return ERROR_FUNCTION_FAILED;
1530 if (!child->Parent) return ERROR_SUCCESS;
1532 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1533 return ERROR_FUNCTION_FAILED;
1535 return add_folder_child( parent, child );
1538 static UINT load_all_folders( MSIPACKAGE *package )
1540 static const WCHAR query[] = {
1541 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1542 '`','D','i','r','e','c','t','o','r','y','`',0};
1543 MSIQUERY *view;
1544 UINT r;
1546 if (!list_empty(&package->folders))
1547 return ERROR_SUCCESS;
1549 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1550 if (r != ERROR_SUCCESS)
1551 return r;
1553 r = MSI_IterateRecords( view, NULL, load_folder, package );
1554 if (r != ERROR_SUCCESS)
1556 msiobj_release( &view->hdr );
1557 return r;
1559 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1560 msiobj_release( &view->hdr );
1561 return r;
1564 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1566 msi_set_property( package->db, szCostingComplete, szZero );
1567 msi_set_property( package->db, szRootDrive, szCRoot );
1569 load_all_folders( package );
1570 msi_load_all_components( package );
1571 msi_load_all_features( package );
1572 load_all_files( package );
1573 load_all_patches( package );
1574 load_all_media( package );
1576 return ERROR_SUCCESS;
1579 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1581 const WCHAR *action = package->script->Actions[script][index];
1582 ui_actionstart( package, action );
1583 TRACE("executing %s\n", debugstr_w(action));
1584 return ACTION_PerformAction( package, action, script );
1587 static UINT execute_script( MSIPACKAGE *package, UINT script )
1589 UINT i, rc = ERROR_SUCCESS;
1591 TRACE("executing script %u\n", script);
1593 if (!package->script)
1595 ERR("no script!\n");
1596 return ERROR_FUNCTION_FAILED;
1598 if (script == SCRIPT_ROLLBACK)
1600 for (i = package->script->ActionCount[script]; i > 0; i--)
1602 rc = execute_script_action( package, script, i - 1 );
1603 if (rc != ERROR_SUCCESS) break;
1606 else
1608 for (i = 0; i < package->script->ActionCount[script]; i++)
1610 rc = execute_script_action( package, script, i );
1611 if (rc != ERROR_SUCCESS) break;
1614 msi_free_action_script(package, script);
1615 return rc;
1618 static UINT ACTION_FileCost(MSIPACKAGE *package)
1620 return ERROR_SUCCESS;
1623 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1625 MSICOMPONENT *comp;
1626 UINT r;
1628 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1630 if (!comp->ComponentId) continue;
1632 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1633 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1634 &comp->Installed );
1635 if (r == ERROR_SUCCESS) continue;
1637 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1638 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1639 &comp->Installed );
1640 if (r == ERROR_SUCCESS) continue;
1642 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1643 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1644 &comp->Installed );
1645 if (r == ERROR_SUCCESS) continue;
1647 comp->Installed = INSTALLSTATE_ABSENT;
1651 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1653 MSIFEATURE *feature;
1655 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1657 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1659 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1660 feature->Installed = INSTALLSTATE_ABSENT;
1661 else
1662 feature->Installed = state;
1666 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1668 return (feature->Level > 0 && feature->Level <= level);
1671 static BOOL process_state_property(MSIPACKAGE* package, int level,
1672 LPCWSTR property, INSTALLSTATE state)
1674 LPWSTR override;
1675 MSIFEATURE *feature;
1677 override = msi_dup_property( package->db, property );
1678 if (!override)
1679 return FALSE;
1681 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1683 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1684 continue;
1686 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1688 if (!strcmpiW( override, szAll ))
1690 if (feature->Installed != state)
1692 feature->Action = state;
1693 feature->ActionRequest = state;
1696 else
1698 LPWSTR ptr = override;
1699 LPWSTR ptr2 = strchrW(override,',');
1701 while (ptr)
1703 int len = ptr2 - ptr;
1705 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1706 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1708 if (feature->Installed != state)
1710 feature->Action = state;
1711 feature->ActionRequest = state;
1713 break;
1715 if (ptr2)
1717 ptr=ptr2+1;
1718 ptr2 = strchrW(ptr,',');
1720 else
1721 break;
1725 msi_free(override);
1726 return TRUE;
1729 static BOOL process_overrides( MSIPACKAGE *package, int level )
1731 static const WCHAR szAddLocal[] =
1732 {'A','D','D','L','O','C','A','L',0};
1733 static const WCHAR szAddSource[] =
1734 {'A','D','D','S','O','U','R','C','E',0};
1735 static const WCHAR szAdvertise[] =
1736 {'A','D','V','E','R','T','I','S','E',0};
1737 BOOL ret = FALSE;
1739 /* all these activation/deactivation things happen in order and things
1740 * later on the list override things earlier on the list.
1742 * 0 INSTALLLEVEL processing
1743 * 1 ADDLOCAL
1744 * 2 REMOVE
1745 * 3 ADDSOURCE
1746 * 4 ADDDEFAULT
1747 * 5 REINSTALL
1748 * 6 ADVERTISE
1749 * 7 COMPADDLOCAL
1750 * 8 COMPADDSOURCE
1751 * 9 FILEADDLOCAL
1752 * 10 FILEADDSOURCE
1753 * 11 FILEADDDEFAULT
1755 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1756 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1757 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1758 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1759 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1761 if (ret)
1762 msi_set_property( package->db, szPreselected, szOne );
1764 return ret;
1767 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1769 int level;
1770 MSICOMPONENT* component;
1771 MSIFEATURE *feature;
1773 TRACE("Checking Install Level\n");
1775 level = msi_get_property_int(package->db, szInstallLevel, 1);
1777 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1779 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1781 if (!is_feature_selected( feature, level )) continue;
1783 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1785 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1787 feature->Action = INSTALLSTATE_SOURCE;
1788 feature->ActionRequest = INSTALLSTATE_SOURCE;
1790 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1792 feature->Action = INSTALLSTATE_ADVERTISED;
1793 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1795 else
1797 feature->Action = INSTALLSTATE_LOCAL;
1798 feature->ActionRequest = INSTALLSTATE_LOCAL;
1802 /* disable child features of unselected parent or follow parent */
1803 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1805 FeatureList *fl;
1807 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1809 if (!is_feature_selected( feature, level ))
1811 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1812 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1814 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1816 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1817 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1818 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1819 fl->feature->Action = feature->Action;
1820 fl->feature->ActionRequest = feature->ActionRequest;
1825 else /* preselected */
1827 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1829 if (!is_feature_selected( feature, level )) continue;
1831 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1833 if (feature->Installed == INSTALLSTATE_ABSENT)
1835 feature->Action = INSTALLSTATE_UNKNOWN;
1836 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1838 else
1840 feature->Action = feature->Installed;
1841 feature->ActionRequest = feature->Installed;
1845 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1847 FeatureList *fl;
1849 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1851 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1852 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1854 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1855 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1856 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1857 fl->feature->Action = feature->Action;
1858 fl->feature->ActionRequest = feature->ActionRequest;
1864 /* now we want to set component state based based on feature state */
1865 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1867 ComponentList *cl;
1869 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1870 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1871 feature->ActionRequest, feature->Action);
1873 if (!is_feature_selected( feature, level )) continue;
1875 /* features with components that have compressed files are made local */
1876 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1878 if (cl->component->ForceLocalState &&
1879 feature->ActionRequest == INSTALLSTATE_SOURCE)
1881 feature->Action = INSTALLSTATE_LOCAL;
1882 feature->ActionRequest = INSTALLSTATE_LOCAL;
1883 break;
1887 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1889 component = cl->component;
1891 switch (feature->ActionRequest)
1893 case INSTALLSTATE_ABSENT:
1894 component->anyAbsent = 1;
1895 break;
1896 case INSTALLSTATE_ADVERTISED:
1897 component->hasAdvertiseFeature = 1;
1898 break;
1899 case INSTALLSTATE_SOURCE:
1900 component->hasSourceFeature = 1;
1901 break;
1902 case INSTALLSTATE_LOCAL:
1903 component->hasLocalFeature = 1;
1904 break;
1905 case INSTALLSTATE_DEFAULT:
1906 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1907 component->hasAdvertiseFeature = 1;
1908 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1909 component->hasSourceFeature = 1;
1910 else
1911 component->hasLocalFeature = 1;
1912 break;
1913 default:
1914 break;
1919 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1921 /* check if it's local or source */
1922 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1923 (component->hasLocalFeature || component->hasSourceFeature))
1925 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1926 !component->ForceLocalState)
1928 component->Action = INSTALLSTATE_SOURCE;
1929 component->ActionRequest = INSTALLSTATE_SOURCE;
1931 else
1933 component->Action = INSTALLSTATE_LOCAL;
1934 component->ActionRequest = INSTALLSTATE_LOCAL;
1936 continue;
1939 /* if any feature is local, the component must be local too */
1940 if (component->hasLocalFeature)
1942 component->Action = INSTALLSTATE_LOCAL;
1943 component->ActionRequest = INSTALLSTATE_LOCAL;
1944 continue;
1946 if (component->hasSourceFeature)
1948 component->Action = INSTALLSTATE_SOURCE;
1949 component->ActionRequest = INSTALLSTATE_SOURCE;
1950 continue;
1952 if (component->hasAdvertiseFeature)
1954 component->Action = INSTALLSTATE_ADVERTISED;
1955 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1956 continue;
1958 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1959 if (component->anyAbsent &&
1960 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1962 component->Action = INSTALLSTATE_ABSENT;
1963 component->ActionRequest = INSTALLSTATE_ABSENT;
1967 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1969 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1971 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1972 component->Action = INSTALLSTATE_LOCAL;
1973 component->ActionRequest = INSTALLSTATE_LOCAL;
1976 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1977 component->Installed == INSTALLSTATE_SOURCE &&
1978 component->hasSourceFeature)
1980 component->Action = INSTALLSTATE_UNKNOWN;
1981 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1984 TRACE("component %s (installed %d request %d action %d)\n",
1985 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1988 return ERROR_SUCCESS;
1991 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1993 MSIPACKAGE *package = param;
1994 LPCWSTR name;
1995 MSIFEATURE *feature;
1997 name = MSI_RecordGetString( row, 1 );
1999 feature = msi_get_loaded_feature( package, name );
2000 if (!feature)
2001 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2002 else
2004 LPCWSTR Condition;
2005 Condition = MSI_RecordGetString(row,3);
2007 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2009 int level = MSI_RecordGetInteger(row,2);
2010 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2011 feature->Level = level;
2014 return ERROR_SUCCESS;
2017 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2019 static const WCHAR name[] = {'\\',0};
2020 VS_FIXEDFILEINFO *ptr, *ret;
2021 LPVOID version;
2022 DWORD versize, handle;
2023 UINT sz;
2025 versize = GetFileVersionInfoSizeW( filename, &handle );
2026 if (!versize)
2027 return NULL;
2029 version = msi_alloc( versize );
2030 if (!version)
2031 return NULL;
2033 GetFileVersionInfoW( filename, 0, versize, version );
2035 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2037 msi_free( version );
2038 return NULL;
2041 ret = msi_alloc( sz );
2042 memcpy( ret, ptr, sz );
2044 msi_free( version );
2045 return ret;
2048 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2050 DWORD ms, ls;
2052 msi_parse_version_string( version, &ms, &ls );
2054 if (fi->dwFileVersionMS > ms) return 1;
2055 else if (fi->dwFileVersionMS < ms) return -1;
2056 else if (fi->dwFileVersionLS > ls) return 1;
2057 else if (fi->dwFileVersionLS < ls) return -1;
2058 return 0;
2061 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2063 DWORD ms1, ms2;
2065 msi_parse_version_string( ver1, &ms1, NULL );
2066 msi_parse_version_string( ver2, &ms2, NULL );
2068 if (ms1 > ms2) return 1;
2069 else if (ms1 < ms2) return -1;
2070 return 0;
2073 DWORD msi_get_disk_file_size( LPCWSTR filename )
2075 HANDLE file;
2076 DWORD size;
2078 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2079 if (file == INVALID_HANDLE_VALUE)
2080 return INVALID_FILE_SIZE;
2082 size = GetFileSize( file, NULL );
2083 TRACE("size is %u\n", size);
2084 CloseHandle( file );
2085 return size;
2088 BOOL msi_file_hash_matches( MSIFILE *file )
2090 UINT r;
2091 MSIFILEHASHINFO hash;
2093 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2094 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2095 if (r != ERROR_SUCCESS)
2096 return FALSE;
2098 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2101 static WCHAR *get_temp_dir( void )
2103 static UINT id;
2104 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2106 GetTempPathW( MAX_PATH, tmp );
2107 for (;;)
2109 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2110 if (CreateDirectoryW( dir, NULL )) break;
2112 return strdupW( dir );
2116 * msi_build_directory_name()
2118 * This function is to save messing round with directory names
2119 * It handles adding backslashes between path segments,
2120 * and can add \ at the end of the directory name if told to.
2122 * It takes a variable number of arguments.
2123 * It always allocates a new string for the result, so make sure
2124 * to free the return value when finished with it.
2126 * The first arg is the number of path segments that follow.
2127 * The arguments following count are a list of path segments.
2128 * A path segment may be NULL.
2130 * Path segments will be added with a \ separating them.
2131 * A \ will not be added after the last segment, however if the
2132 * last segment is NULL, then the last character will be a \
2134 WCHAR *msi_build_directory_name( DWORD count, ... )
2136 DWORD sz = 1, i;
2137 WCHAR *dir;
2138 va_list va;
2140 va_start( va, count );
2141 for (i = 0; i < count; i++)
2143 const WCHAR *str = va_arg( va, const WCHAR * );
2144 if (str) sz += strlenW( str ) + 1;
2146 va_end( va );
2148 dir = msi_alloc( sz * sizeof(WCHAR) );
2149 dir[0] = 0;
2151 va_start( va, count );
2152 for (i = 0; i < count; i++)
2154 const WCHAR *str = va_arg( va, const WCHAR * );
2155 if (!str) continue;
2156 strcatW( dir, str );
2157 if ( i + 1 != count && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2159 va_end( va );
2160 return dir;
2163 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2165 MSIASSEMBLY *assembly = file->Component->assembly;
2167 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2169 msi_free( file->TargetPath );
2170 if (assembly && !assembly->application)
2172 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2173 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2174 msi_track_tempfile( package, file->TargetPath );
2176 else
2178 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2179 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2182 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2185 static UINT calculate_file_cost( MSIPACKAGE *package )
2187 VS_FIXEDFILEINFO *file_version;
2188 WCHAR *font_version;
2189 MSIFILE *file;
2191 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2193 MSICOMPONENT *comp = file->Component;
2194 DWORD file_size;
2196 if (!comp->Enabled) continue;
2198 if (file->IsCompressed)
2199 comp->ForceLocalState = TRUE;
2201 set_target_path( package, file );
2203 if ((comp->assembly && !comp->assembly->installed) ||
2204 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2206 comp->Cost += file->FileSize;
2207 continue;
2209 file_size = msi_get_disk_file_size( file->TargetPath );
2211 if (file->Version)
2213 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2215 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2217 comp->Cost += file->FileSize - file_size;
2219 msi_free( file_version );
2220 continue;
2222 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2224 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2226 comp->Cost += file->FileSize - file_size;
2228 msi_free( font_version );
2229 continue;
2232 if (file_size != file->FileSize)
2234 comp->Cost += file->FileSize - file_size;
2237 return ERROR_SUCCESS;
2240 WCHAR *msi_normalize_path( const WCHAR *in )
2242 const WCHAR *p = in;
2243 WCHAR *q, *ret;
2244 int n, len = strlenW( in ) + 2;
2246 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2248 len = 0;
2249 while (1)
2251 /* copy until the end of the string or a space */
2252 while (*p != ' ' && (*q = *p))
2254 p++, len++;
2255 /* reduce many backslashes to one */
2256 if (*p != '\\' || *q != '\\')
2257 q++;
2260 /* quit at the end of the string */
2261 if (!*p)
2262 break;
2264 /* count the number of spaces */
2265 n = 0;
2266 while (p[n] == ' ')
2267 n++;
2269 /* if it's leading or trailing space, skip it */
2270 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2271 p += n;
2272 else /* copy n spaces */
2273 while (n && (*q++ = *p++)) n--;
2275 while (q - ret > 0 && q[-1] == ' ') q--;
2276 if (q - ret > 0 && q[-1] != '\\')
2278 q[0] = '\\';
2279 q[1] = 0;
2281 return ret;
2284 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2286 FolderList *fl;
2287 MSIFOLDER *folder, *parent, *child;
2288 WCHAR *path, *normalized_path;
2290 TRACE("resolving %s\n", debugstr_w(name));
2292 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2294 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2296 if (!load_prop || !(path = msi_dup_property( package->db, szTargetDir )))
2298 path = msi_dup_property( package->db, szRootDrive );
2301 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2303 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2305 parent = msi_get_loaded_folder( package, folder->Parent );
2306 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2308 else
2309 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2311 normalized_path = msi_normalize_path( path );
2312 msi_free( path );
2313 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2315 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2316 msi_free( normalized_path );
2317 return;
2319 msi_set_property( package->db, folder->Directory, normalized_path );
2320 msi_free( folder->ResolvedTarget );
2321 folder->ResolvedTarget = normalized_path;
2323 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2325 child = fl->folder;
2326 msi_resolve_target_folder( package, child->Directory, load_prop );
2328 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2331 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2333 static const WCHAR query[] = {
2334 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2335 '`','C','o','n','d','i','t','i','o','n','`',0};
2336 static const WCHAR szOutOfDiskSpace[] = {
2337 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2338 MSICOMPONENT *comp;
2339 MSIQUERY *view;
2340 LPWSTR level;
2341 UINT rc;
2343 TRACE("Building directory properties\n");
2344 msi_resolve_target_folder( package, szTargetDir, TRUE );
2346 TRACE("Evaluating component conditions\n");
2347 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2349 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2351 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2352 comp->Enabled = FALSE;
2354 else
2355 comp->Enabled = TRUE;
2358 /* read components states from the registry */
2359 ACTION_GetComponentInstallStates(package);
2360 ACTION_GetFeatureInstallStates(package);
2362 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2364 TRACE("Evaluating feature conditions\n");
2366 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2367 if (rc == ERROR_SUCCESS)
2369 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2370 msiobj_release( &view->hdr );
2371 if (rc != ERROR_SUCCESS)
2372 return rc;
2376 TRACE("Calculating file cost\n");
2377 calculate_file_cost( package );
2379 msi_set_property( package->db, szCostingComplete, szOne );
2380 /* set default run level if not set */
2381 level = msi_dup_property( package->db, szInstallLevel );
2382 if (!level)
2383 msi_set_property( package->db, szInstallLevel, szOne );
2384 msi_free(level);
2386 /* FIXME: check volume disk space */
2387 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2389 return MSI_SetFeatureStates(package);
2392 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, DWORD *size)
2394 LPSTR data = NULL;
2396 if (!value)
2398 data = (LPSTR)strdupW(szEmpty);
2399 *size = sizeof(szEmpty);
2400 *type = REG_SZ;
2401 return data;
2403 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2405 if (value[1]=='x')
2407 LPWSTR ptr;
2408 CHAR byte[5];
2409 LPWSTR deformated = NULL;
2410 int count;
2412 deformat_string(package, &value[2], &deformated);
2414 /* binary value type */
2415 ptr = deformated;
2416 *type = REG_BINARY;
2417 if (strlenW(ptr)%2)
2418 *size = (strlenW(ptr)/2)+1;
2419 else
2420 *size = strlenW(ptr)/2;
2422 data = msi_alloc(*size);
2424 byte[0] = '0';
2425 byte[1] = 'x';
2426 byte[4] = 0;
2427 count = 0;
2428 /* if uneven pad with a zero in front */
2429 if (strlenW(ptr)%2)
2431 byte[2]= '0';
2432 byte[3]= *ptr;
2433 ptr++;
2434 data[count] = (BYTE)strtol(byte,NULL,0);
2435 count ++;
2436 TRACE("Uneven byte count\n");
2438 while (*ptr)
2440 byte[2]= *ptr;
2441 ptr++;
2442 byte[3]= *ptr;
2443 ptr++;
2444 data[count] = (BYTE)strtol(byte,NULL,0);
2445 count ++;
2447 msi_free(deformated);
2449 TRACE("Data %i bytes(%i)\n",*size,count);
2451 else
2453 LPWSTR deformated;
2454 LPWSTR p;
2455 DWORD d = 0;
2456 deformat_string(package, &value[1], &deformated);
2458 *type=REG_DWORD;
2459 *size = sizeof(DWORD);
2460 data = msi_alloc(*size);
2461 p = deformated;
2462 if (*p == '-')
2463 p++;
2464 while (*p)
2466 if ( (*p < '0') || (*p > '9') )
2467 break;
2468 d *= 10;
2469 d += (*p - '0');
2470 p++;
2472 if (deformated[0] == '-')
2473 d = -d;
2474 *(LPDWORD)data = d;
2475 TRACE("DWORD %i\n",*(LPDWORD)data);
2477 msi_free(deformated);
2480 else
2482 static const WCHAR szMulti[] = {'[','~',']',0};
2483 LPCWSTR ptr;
2484 *type=REG_SZ;
2486 if (value[0]=='#')
2488 if (value[1]=='%')
2490 ptr = &value[2];
2491 *type=REG_EXPAND_SZ;
2493 else
2494 ptr = &value[1];
2496 else
2497 ptr=value;
2499 if (strstrW(value, szMulti))
2500 *type = REG_MULTI_SZ;
2502 /* remove initial delimiter */
2503 if (!strncmpW(value, szMulti, 3))
2504 ptr = value + 3;
2506 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2508 /* add double NULL terminator */
2509 if (*type == REG_MULTI_SZ)
2511 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2512 data = msi_realloc_zero(data, *size);
2515 return data;
2518 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2520 const WCHAR *ret;
2522 switch (root)
2524 case -1:
2525 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2527 *root_key = HKEY_LOCAL_MACHINE;
2528 ret = szHLM;
2530 else
2532 *root_key = HKEY_CURRENT_USER;
2533 ret = szHCU;
2535 break;
2536 case 0:
2537 *root_key = HKEY_CLASSES_ROOT;
2538 ret = szHCR;
2539 break;
2540 case 1:
2541 *root_key = HKEY_CURRENT_USER;
2542 ret = szHCU;
2543 break;
2544 case 2:
2545 *root_key = HKEY_LOCAL_MACHINE;
2546 ret = szHLM;
2547 break;
2548 case 3:
2549 *root_key = HKEY_USERS;
2550 ret = szHU;
2551 break;
2552 default:
2553 ERR("Unknown root %i\n", root);
2554 return NULL;
2557 return ret;
2560 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2562 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2563 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2565 if ((is_64bit || is_wow64) &&
2566 !(comp->Attributes & msidbComponentAttributes64bit) &&
2567 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2569 UINT size;
2570 WCHAR *path_32node;
2572 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2573 if (!(path_32node = msi_alloc( size ))) return NULL;
2575 memcpy( path_32node, path, len * sizeof(WCHAR) );
2576 strcpyW( path_32node + len, szWow6432Node );
2577 strcatW( path_32node, szBackSlash );
2578 strcatW( path_32node, path + len );
2579 return path_32node;
2581 return strdupW( path );
2584 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2586 REGSAM access = KEY_ALL_ACCESS;
2587 WCHAR *subkey, *p, *q;
2588 HKEY hkey, ret = NULL;
2589 LONG res;
2591 if (is_wow64) access |= KEY_WOW64_64KEY;
2593 if (!(subkey = strdupW( path ))) return NULL;
2594 p = subkey;
2595 if ((q = strchrW( p, '\\' ))) *q = 0;
2596 if (create)
2597 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2598 else
2599 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2600 if (res)
2602 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2603 msi_free( subkey );
2604 return NULL;
2606 if (q && q[1])
2608 ret = open_key( hkey, q + 1, create );
2609 RegCloseKey( hkey );
2611 else ret = hkey;
2612 msi_free( subkey );
2613 return ret;
2616 static BOOL is_special_entry( const WCHAR *name )
2618 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2621 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2623 MSIPACKAGE *package = param;
2624 LPSTR value;
2625 HKEY root_key, hkey;
2626 DWORD type,size;
2627 LPWSTR deformated, uikey, keypath;
2628 LPCWSTR szRoot, component, name, key;
2629 MSICOMPONENT *comp;
2630 MSIRECORD * uirow;
2631 INT root;
2632 BOOL check_first = FALSE;
2633 UINT rc;
2635 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2637 component = MSI_RecordGetString(row, 6);
2638 comp = msi_get_loaded_component(package,component);
2639 if (!comp)
2640 return ERROR_SUCCESS;
2642 comp->Action = msi_get_component_action( package, comp );
2643 if (comp->Action != INSTALLSTATE_LOCAL)
2645 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2646 return ERROR_SUCCESS;
2649 name = MSI_RecordGetString(row, 4);
2650 if( MSI_RecordIsNull(row,5) && name )
2652 /* null values can have special meanings */
2653 if (name[0]=='-' && name[1] == 0)
2654 return ERROR_SUCCESS;
2655 if ((name[0] == '+' || name[0] == '*') && !name[1])
2656 check_first = TRUE;
2659 root = MSI_RecordGetInteger(row,2);
2660 key = MSI_RecordGetString(row, 3);
2662 szRoot = get_root_key( package, root, &root_key );
2663 if (!szRoot)
2664 return ERROR_SUCCESS;
2666 deformat_string(package, key , &deformated);
2667 size = strlenW(deformated) + strlenW(szRoot) + 1;
2668 uikey = msi_alloc(size*sizeof(WCHAR));
2669 strcpyW(uikey,szRoot);
2670 strcatW(uikey,deformated);
2672 keypath = get_keypath( comp, root_key, deformated );
2673 msi_free( deformated );
2674 if (!(hkey = open_key( root_key, keypath, TRUE )))
2676 ERR("Could not create key %s\n", debugstr_w(keypath));
2677 msi_free(uikey);
2678 msi_free(keypath);
2679 return ERROR_FUNCTION_FAILED;
2681 value = parse_value(package, MSI_RecordGetString(row, 5), &type, &size);
2682 deformat_string(package, name, &deformated);
2684 if (!is_special_entry( name ))
2686 if (!check_first)
2688 TRACE("Setting value %s of %s\n", debugstr_w(deformated),
2689 debugstr_w(uikey));
2690 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2692 else
2694 DWORD sz = 0;
2695 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2696 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2698 TRACE("value %s of %s checked already exists\n", debugstr_w(deformated),
2699 debugstr_w(uikey));
2701 else
2703 TRACE("Checked and setting value %s of %s\n", debugstr_w(deformated),
2704 debugstr_w(uikey));
2705 if (deformated || size)
2706 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2710 RegCloseKey(hkey);
2712 uirow = MSI_CreateRecord(3);
2713 MSI_RecordSetStringW(uirow,2,deformated);
2714 MSI_RecordSetStringW(uirow,1,uikey);
2715 if (type == REG_SZ || type == REG_EXPAND_SZ)
2716 MSI_RecordSetStringW(uirow, 3, (LPWSTR)value);
2717 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2718 msiobj_release( &uirow->hdr );
2720 msi_free(value);
2721 msi_free(deformated);
2722 msi_free(uikey);
2723 msi_free(keypath);
2725 return ERROR_SUCCESS;
2728 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2730 static const WCHAR query[] = {
2731 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2732 '`','R','e','g','i','s','t','r','y','`',0};
2733 MSIQUERY *view;
2734 UINT rc;
2736 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2737 if (rc != ERROR_SUCCESS)
2738 return ERROR_SUCCESS;
2740 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2741 msiobj_release(&view->hdr);
2742 return rc;
2745 static void delete_key( HKEY root, const WCHAR *path )
2747 REGSAM access = 0;
2748 WCHAR *subkey, *p;
2749 HKEY hkey;
2750 LONG res;
2752 if (is_wow64) access |= KEY_WOW64_64KEY;
2754 if (!(subkey = strdupW( path ))) return;
2755 for (;;)
2757 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2758 hkey = open_key( root, subkey, FALSE );
2759 if (!hkey) break;
2760 if (p && p[1])
2761 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2762 else
2763 res = RegDeleteKeyExW( root, subkey, access, 0 );
2764 if (res)
2766 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2767 break;
2769 if (p && p[1]) RegCloseKey( hkey );
2770 else break;
2772 msi_free( subkey );
2775 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2777 LONG res;
2778 HKEY hkey;
2779 DWORD num_subkeys, num_values;
2781 if ((hkey = open_key( root, path, FALSE )))
2783 if ((res = RegDeleteValueW( hkey, value )))
2784 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2786 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2787 NULL, NULL, NULL, NULL );
2788 RegCloseKey( hkey );
2789 if (!res && !num_subkeys && !num_values)
2791 TRACE("removing empty key %s\n", debugstr_w(path));
2792 delete_key( root, path );
2797 static void delete_tree( HKEY root, const WCHAR *path )
2799 LONG res;
2800 HKEY hkey;
2802 if (!(hkey = open_key( root, path, FALSE ))) return;
2803 res = RegDeleteTreeW( hkey, NULL );
2804 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
2805 delete_key( root, path );
2806 RegCloseKey( hkey );
2809 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2811 MSIPACKAGE *package = param;
2812 LPCWSTR component, name, key_str, root_key_str;
2813 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2814 MSICOMPONENT *comp;
2815 MSIRECORD *uirow;
2816 BOOL delete_key = FALSE;
2817 HKEY hkey_root;
2818 UINT size;
2819 INT root;
2821 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2823 component = MSI_RecordGetString( row, 6 );
2824 comp = msi_get_loaded_component( package, component );
2825 if (!comp)
2826 return ERROR_SUCCESS;
2828 comp->Action = msi_get_component_action( package, comp );
2829 if (comp->Action != INSTALLSTATE_ABSENT)
2831 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2832 return ERROR_SUCCESS;
2835 name = MSI_RecordGetString( row, 4 );
2836 if (MSI_RecordIsNull( row, 5 ) && name )
2838 if (name[0] == '+' && !name[1])
2839 return ERROR_SUCCESS;
2840 if ((name[0] == '-' || name[0] == '*') && !name[1])
2842 delete_key = TRUE;
2843 name = NULL;
2847 root = MSI_RecordGetInteger( row, 2 );
2848 key_str = MSI_RecordGetString( row, 3 );
2850 root_key_str = get_root_key( package, root, &hkey_root );
2851 if (!root_key_str)
2852 return ERROR_SUCCESS;
2854 deformat_string( package, key_str, &deformated_key );
2855 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2856 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2857 strcpyW( ui_key_str, root_key_str );
2858 strcatW( ui_key_str, deformated_key );
2860 deformat_string( package, name, &deformated_name );
2862 keypath = get_keypath( comp, hkey_root, deformated_key );
2863 msi_free( deformated_key );
2864 if (delete_key) delete_tree( hkey_root, keypath );
2865 else delete_value( hkey_root, keypath, deformated_name );
2866 msi_free( keypath );
2868 uirow = MSI_CreateRecord( 2 );
2869 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2870 MSI_RecordSetStringW( uirow, 2, deformated_name );
2871 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2872 msiobj_release( &uirow->hdr );
2874 msi_free( ui_key_str );
2875 msi_free( deformated_name );
2876 return ERROR_SUCCESS;
2879 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2881 MSIPACKAGE *package = param;
2882 LPCWSTR component, name, key_str, root_key_str;
2883 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2884 MSICOMPONENT *comp;
2885 MSIRECORD *uirow;
2886 BOOL delete_key = FALSE;
2887 HKEY hkey_root;
2888 UINT size;
2889 INT root;
2891 component = MSI_RecordGetString( row, 5 );
2892 comp = msi_get_loaded_component( package, component );
2893 if (!comp)
2894 return ERROR_SUCCESS;
2896 comp->Action = msi_get_component_action( package, comp );
2897 if (comp->Action != INSTALLSTATE_LOCAL)
2899 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2900 return ERROR_SUCCESS;
2903 if ((name = MSI_RecordGetString( row, 4 )))
2905 if (name[0] == '-' && !name[1])
2907 delete_key = TRUE;
2908 name = NULL;
2912 root = MSI_RecordGetInteger( row, 2 );
2913 key_str = MSI_RecordGetString( row, 3 );
2915 root_key_str = get_root_key( package, root, &hkey_root );
2916 if (!root_key_str)
2917 return ERROR_SUCCESS;
2919 deformat_string( package, key_str, &deformated_key );
2920 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2921 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2922 strcpyW( ui_key_str, root_key_str );
2923 strcatW( ui_key_str, deformated_key );
2925 deformat_string( package, name, &deformated_name );
2927 keypath = get_keypath( comp, hkey_root, deformated_key );
2928 msi_free( deformated_key );
2929 if (delete_key) delete_tree( hkey_root, keypath );
2930 else delete_value( hkey_root, keypath, deformated_name );
2931 msi_free( keypath );
2933 uirow = MSI_CreateRecord( 2 );
2934 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2935 MSI_RecordSetStringW( uirow, 2, deformated_name );
2936 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2937 msiobj_release( &uirow->hdr );
2939 msi_free( ui_key_str );
2940 msi_free( deformated_name );
2941 return ERROR_SUCCESS;
2944 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2946 static const WCHAR registry_query[] = {
2947 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2948 '`','R','e','g','i','s','t','r','y','`',0};
2949 static const WCHAR remove_registry_query[] = {
2950 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2951 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2952 MSIQUERY *view;
2953 UINT rc;
2955 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2956 if (rc == ERROR_SUCCESS)
2958 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2959 msiobj_release( &view->hdr );
2960 if (rc != ERROR_SUCCESS)
2961 return rc;
2963 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2964 if (rc == ERROR_SUCCESS)
2966 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2967 msiobj_release( &view->hdr );
2968 if (rc != ERROR_SUCCESS)
2969 return rc;
2971 return ERROR_SUCCESS;
2974 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2976 package->script->CurrentlyScripting = TRUE;
2978 return ERROR_SUCCESS;
2982 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2984 static const WCHAR query[]= {
2985 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2986 '`','R','e','g','i','s','t','r','y','`',0};
2987 MSICOMPONENT *comp;
2988 DWORD total = 0, count = 0;
2989 MSIQUERY *view;
2990 MSIFEATURE *feature;
2991 MSIFILE *file;
2992 UINT rc;
2994 TRACE("InstallValidate\n");
2996 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2997 if (rc == ERROR_SUCCESS)
2999 rc = MSI_IterateRecords( view, &count, NULL, package );
3000 msiobj_release( &view->hdr );
3001 if (rc != ERROR_SUCCESS)
3002 return rc;
3003 total += count * REG_PROGRESS_VALUE;
3005 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3006 total += COMPONENT_PROGRESS_VALUE;
3008 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3009 total += file->FileSize;
3011 msi_ui_progress( package, 0, total, 0, 0 );
3013 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3015 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3016 debugstr_w(feature->Feature), feature->Installed,
3017 feature->ActionRequest, feature->Action);
3019 return ERROR_SUCCESS;
3022 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3024 MSIPACKAGE* package = param;
3025 LPCWSTR cond = NULL;
3026 LPCWSTR message = NULL;
3027 UINT r;
3029 static const WCHAR title[]=
3030 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3032 cond = MSI_RecordGetString(row,1);
3034 r = MSI_EvaluateConditionW(package,cond);
3035 if (r == MSICONDITION_FALSE)
3037 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3039 LPWSTR deformated;
3040 message = MSI_RecordGetString(row,2);
3041 deformat_string(package,message,&deformated);
3042 MessageBoxW(NULL,deformated,title,MB_OK);
3043 msi_free(deformated);
3046 return ERROR_INSTALL_FAILURE;
3049 return ERROR_SUCCESS;
3052 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3054 static const WCHAR query[] = {
3055 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3056 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3057 MSIQUERY *view;
3058 UINT rc;
3060 TRACE("Checking launch conditions\n");
3062 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3063 if (rc != ERROR_SUCCESS)
3064 return ERROR_SUCCESS;
3066 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3067 msiobj_release(&view->hdr);
3068 return rc;
3071 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3074 if (!cmp->KeyPath)
3075 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3077 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3079 static const WCHAR query[] = {
3080 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3081 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3082 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3083 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3084 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3085 MSIRECORD *row;
3086 UINT root, len;
3087 LPWSTR deformated, buffer, deformated_name;
3088 LPCWSTR key, name;
3090 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3091 if (!row)
3092 return NULL;
3094 root = MSI_RecordGetInteger(row,2);
3095 key = MSI_RecordGetString(row, 3);
3096 name = MSI_RecordGetString(row, 4);
3097 deformat_string(package, key , &deformated);
3098 deformat_string(package, name, &deformated_name);
3100 len = strlenW(deformated) + 6;
3101 if (deformated_name)
3102 len+=strlenW(deformated_name);
3104 buffer = msi_alloc( len *sizeof(WCHAR));
3106 if (deformated_name)
3107 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3108 else
3109 sprintfW(buffer,fmt,root,deformated);
3111 msi_free(deformated);
3112 msi_free(deformated_name);
3113 msiobj_release(&row->hdr);
3115 return buffer;
3117 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3119 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3120 return NULL;
3122 else
3124 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3126 if (file)
3127 return strdupW( file->TargetPath );
3129 return NULL;
3132 static HKEY openSharedDLLsKey(void)
3134 HKEY hkey=0;
3135 static const WCHAR path[] =
3136 {'S','o','f','t','w','a','r','e','\\',
3137 'M','i','c','r','o','s','o','f','t','\\',
3138 'W','i','n','d','o','w','s','\\',
3139 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3140 'S','h','a','r','e','d','D','L','L','s',0};
3142 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3143 return hkey;
3146 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3148 HKEY hkey;
3149 DWORD count=0;
3150 DWORD type;
3151 DWORD sz = sizeof(count);
3152 DWORD rc;
3154 hkey = openSharedDLLsKey();
3155 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3156 if (rc != ERROR_SUCCESS)
3157 count = 0;
3158 RegCloseKey(hkey);
3159 return count;
3162 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3164 HKEY hkey;
3166 hkey = openSharedDLLsKey();
3167 if (count > 0)
3168 msi_reg_set_val_dword( hkey, path, count );
3169 else
3170 RegDeleteValueW(hkey,path);
3171 RegCloseKey(hkey);
3172 return count;
3175 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3177 MSIFEATURE *feature;
3178 INT count = 0;
3179 BOOL write = FALSE;
3181 /* only refcount DLLs */
3182 if (comp->KeyPath == NULL ||
3183 comp->assembly ||
3184 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3185 comp->Attributes & msidbComponentAttributesODBCDataSource)
3186 write = FALSE;
3187 else
3189 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3190 write = (count > 0);
3192 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3193 write = TRUE;
3196 /* increment counts */
3197 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3199 ComponentList *cl;
3201 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3202 continue;
3204 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3206 if ( cl->component == comp )
3207 count++;
3211 /* decrement counts */
3212 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3214 ComponentList *cl;
3216 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3217 continue;
3219 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3221 if ( cl->component == comp )
3222 count--;
3226 /* ref count all the files in the component */
3227 if (write)
3229 MSIFILE *file;
3231 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3233 if (file->Component == comp)
3234 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3238 /* add a count for permanent */
3239 if (comp->Attributes & msidbComponentAttributesPermanent)
3240 count ++;
3242 comp->RefCount = count;
3244 if (write)
3245 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3248 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3250 if (comp->assembly)
3252 const WCHAR prefixW[] = {'<','\\',0};
3253 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3254 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3256 if (keypath)
3258 strcpyW( keypath, prefixW );
3259 strcatW( keypath, comp->assembly->display_name );
3261 return keypath;
3263 return resolve_keypath( package, comp );
3266 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3268 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3269 UINT rc;
3270 MSICOMPONENT *comp;
3271 HKEY hkey;
3273 TRACE("\n");
3275 squash_guid(package->ProductCode,squished_pc);
3276 msi_set_sourcedir_props(package, FALSE);
3278 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3280 MSIRECORD *uirow;
3281 INSTALLSTATE action;
3283 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3284 if (!comp->ComponentId)
3285 continue;
3287 squash_guid( comp->ComponentId, squished_cc );
3288 msi_free( comp->FullKeypath );
3289 comp->FullKeypath = build_full_keypath( package, comp );
3291 ACTION_RefCountComponent( package, comp );
3293 if (package->need_rollback) action = comp->Installed;
3294 else action = comp->ActionRequest;
3296 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3297 debugstr_w(comp->Component), debugstr_w(squished_cc),
3298 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3300 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3302 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3303 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3304 else
3305 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3307 if (rc != ERROR_SUCCESS)
3308 continue;
3310 if (comp->Attributes & msidbComponentAttributesPermanent)
3312 static const WCHAR szPermKey[] =
3313 { '0','0','0','0','0','0','0','0','0','0','0','0',
3314 '0','0','0','0','0','0','0','0','0','0','0','0',
3315 '0','0','0','0','0','0','0','0',0 };
3317 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3319 if (action == INSTALLSTATE_LOCAL)
3320 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3321 else
3323 MSIFILE *file;
3324 MSIRECORD *row;
3325 LPWSTR ptr, ptr2;
3326 WCHAR source[MAX_PATH];
3327 WCHAR base[MAX_PATH];
3328 LPWSTR sourcepath;
3330 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3331 static const WCHAR query[] = {
3332 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3333 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3334 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3335 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3336 '`','D','i','s','k','I','d','`',0};
3338 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3339 continue;
3341 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3342 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3343 ptr2 = strrchrW(source, '\\') + 1;
3344 msiobj_release(&row->hdr);
3346 lstrcpyW(base, package->PackagePath);
3347 ptr = strrchrW(base, '\\');
3348 *(ptr + 1) = '\0';
3350 sourcepath = msi_resolve_file_source(package, file);
3351 ptr = sourcepath + lstrlenW(base);
3352 lstrcpyW(ptr2, ptr);
3353 msi_free(sourcepath);
3355 msi_reg_set_val_str(hkey, squished_pc, source);
3357 RegCloseKey(hkey);
3359 else if (action == INSTALLSTATE_ABSENT)
3361 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3362 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3363 else
3364 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3367 /* UI stuff */
3368 uirow = MSI_CreateRecord(3);
3369 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3370 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3371 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3372 msi_ui_actiondata( package, szProcessComponents, uirow );
3373 msiobj_release( &uirow->hdr );
3375 return ERROR_SUCCESS;
3378 typedef struct {
3379 CLSID clsid;
3380 LPWSTR source;
3382 LPWSTR path;
3383 ITypeLib *ptLib;
3384 } typelib_struct;
3386 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3387 LPWSTR lpszName, LONG_PTR lParam)
3389 TLIBATTR *attr;
3390 typelib_struct *tl_struct = (typelib_struct*) lParam;
3391 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3392 int sz;
3393 HRESULT res;
3395 if (!IS_INTRESOURCE(lpszName))
3397 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3398 return TRUE;
3401 sz = strlenW(tl_struct->source)+4;
3402 sz *= sizeof(WCHAR);
3404 if ((INT_PTR)lpszName == 1)
3405 tl_struct->path = strdupW(tl_struct->source);
3406 else
3408 tl_struct->path = msi_alloc(sz);
3409 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3412 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3413 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3414 if (FAILED(res))
3416 msi_free(tl_struct->path);
3417 tl_struct->path = NULL;
3419 return TRUE;
3422 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3423 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3425 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3426 return FALSE;
3429 msi_free(tl_struct->path);
3430 tl_struct->path = NULL;
3432 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3433 ITypeLib_Release(tl_struct->ptLib);
3435 return TRUE;
3438 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3440 MSIPACKAGE* package = param;
3441 LPCWSTR component;
3442 MSICOMPONENT *comp;
3443 MSIFILE *file;
3444 typelib_struct tl_struct;
3445 ITypeLib *tlib;
3446 HMODULE module;
3447 HRESULT hr;
3449 component = MSI_RecordGetString(row,3);
3450 comp = msi_get_loaded_component(package,component);
3451 if (!comp)
3452 return ERROR_SUCCESS;
3454 comp->Action = msi_get_component_action( package, comp );
3455 if (comp->Action != INSTALLSTATE_LOCAL)
3457 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3458 return ERROR_SUCCESS;
3461 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3463 TRACE("component has no key path\n");
3464 return ERROR_SUCCESS;
3466 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3468 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3469 if (module)
3471 LPCWSTR guid;
3472 guid = MSI_RecordGetString(row,1);
3473 CLSIDFromString( guid, &tl_struct.clsid);
3474 tl_struct.source = strdupW( file->TargetPath );
3475 tl_struct.path = NULL;
3477 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3478 (LONG_PTR)&tl_struct);
3480 if (tl_struct.path)
3482 LPCWSTR helpid, help_path = NULL;
3483 HRESULT res;
3485 helpid = MSI_RecordGetString(row,6);
3487 if (helpid) help_path = msi_get_target_folder( package, helpid );
3488 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3490 if (FAILED(res))
3491 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3492 else
3493 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3495 ITypeLib_Release(tl_struct.ptLib);
3496 msi_free(tl_struct.path);
3498 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3500 FreeLibrary(module);
3501 msi_free(tl_struct.source);
3503 else
3505 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3506 if (FAILED(hr))
3508 ERR("Failed to load type library: %08x\n", hr);
3509 return ERROR_INSTALL_FAILURE;
3512 ITypeLib_Release(tlib);
3515 return ERROR_SUCCESS;
3518 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3520 static const WCHAR query[] = {
3521 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3522 '`','T','y','p','e','L','i','b','`',0};
3523 MSIQUERY *view;
3524 UINT rc;
3526 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3527 if (rc != ERROR_SUCCESS)
3528 return ERROR_SUCCESS;
3530 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3531 msiobj_release(&view->hdr);
3532 return rc;
3535 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3537 MSIPACKAGE *package = param;
3538 LPCWSTR component, guid;
3539 MSICOMPONENT *comp;
3540 GUID libid;
3541 UINT version;
3542 LCID language;
3543 SYSKIND syskind;
3544 HRESULT hr;
3546 component = MSI_RecordGetString( row, 3 );
3547 comp = msi_get_loaded_component( package, component );
3548 if (!comp)
3549 return ERROR_SUCCESS;
3551 comp->Action = msi_get_component_action( package, comp );
3552 if (comp->Action != INSTALLSTATE_ABSENT)
3554 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3555 return ERROR_SUCCESS;
3557 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3559 guid = MSI_RecordGetString( row, 1 );
3560 CLSIDFromString( guid, &libid );
3561 version = MSI_RecordGetInteger( row, 4 );
3562 language = MSI_RecordGetInteger( row, 2 );
3564 #ifdef _WIN64
3565 syskind = SYS_WIN64;
3566 #else
3567 syskind = SYS_WIN32;
3568 #endif
3570 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3571 if (FAILED(hr))
3573 WARN("Failed to unregister typelib: %08x\n", hr);
3576 return ERROR_SUCCESS;
3579 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3581 static const WCHAR query[] = {
3582 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3583 '`','T','y','p','e','L','i','b','`',0};
3584 MSIQUERY *view;
3585 UINT rc;
3587 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3588 if (rc != ERROR_SUCCESS)
3589 return ERROR_SUCCESS;
3591 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3592 msiobj_release( &view->hdr );
3593 return rc;
3596 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3598 static const WCHAR szlnk[] = {'.','l','n','k',0};
3599 LPCWSTR directory, extension, link_folder;
3600 LPWSTR link_file, filename;
3602 directory = MSI_RecordGetString( row, 2 );
3603 link_folder = msi_get_target_folder( package, directory );
3604 if (!link_folder)
3606 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3607 return NULL;
3609 /* may be needed because of a bug somewhere else */
3610 msi_create_full_path( link_folder );
3612 filename = msi_dup_record_field( row, 3 );
3613 msi_reduce_to_long_filename( filename );
3615 extension = strchrW( filename, '.' );
3616 if (!extension || strcmpiW( extension, szlnk ))
3618 int len = strlenW( filename );
3619 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3620 memcpy( filename + len, szlnk, sizeof(szlnk) );
3622 link_file = msi_build_directory_name( 2, link_folder, filename );
3623 msi_free( filename );
3625 return link_file;
3628 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3630 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3631 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3632 WCHAR *folder, *dest, *path;
3634 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3635 folder = msi_dup_property( package->db, szWindowsFolder );
3636 else
3638 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3639 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3640 msi_free( appdata );
3642 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3643 msi_create_full_path( dest );
3644 path = msi_build_directory_name( 2, dest, icon_name );
3645 msi_free( folder );
3646 msi_free( dest );
3647 return path;
3650 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3652 MSIPACKAGE *package = param;
3653 LPWSTR link_file, deformated, path;
3654 LPCWSTR component, target;
3655 MSICOMPONENT *comp;
3656 IShellLinkW *sl = NULL;
3657 IPersistFile *pf = NULL;
3658 HRESULT res;
3660 component = MSI_RecordGetString(row, 4);
3661 comp = msi_get_loaded_component(package, component);
3662 if (!comp)
3663 return ERROR_SUCCESS;
3665 comp->Action = msi_get_component_action( package, comp );
3666 if (comp->Action != INSTALLSTATE_LOCAL)
3668 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3669 return ERROR_SUCCESS;
3671 msi_ui_actiondata( package, szCreateShortcuts, row );
3673 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3674 &IID_IShellLinkW, (LPVOID *) &sl );
3676 if (FAILED( res ))
3678 ERR("CLSID_ShellLink not available\n");
3679 goto err;
3682 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3683 if (FAILED( res ))
3685 ERR("QueryInterface(IID_IPersistFile) failed\n");
3686 goto err;
3689 target = MSI_RecordGetString(row, 5);
3690 if (strchrW(target, '['))
3692 deformat_string( package, target, &path );
3693 TRACE("target path is %s\n", debugstr_w(path));
3694 IShellLinkW_SetPath( sl, path );
3695 msi_free( path );
3697 else
3699 FIXME("poorly handled shortcut format, advertised shortcut\n");
3700 IShellLinkW_SetPath(sl,comp->FullKeypath);
3703 if (!MSI_RecordIsNull(row,6))
3705 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3706 deformat_string(package, arguments, &deformated);
3707 IShellLinkW_SetArguments(sl,deformated);
3708 msi_free(deformated);
3711 if (!MSI_RecordIsNull(row,7))
3713 LPCWSTR description = MSI_RecordGetString(row, 7);
3714 IShellLinkW_SetDescription(sl, description);
3717 if (!MSI_RecordIsNull(row,8))
3718 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3720 if (!MSI_RecordIsNull(row,9))
3722 INT index;
3723 LPCWSTR icon = MSI_RecordGetString(row, 9);
3725 path = msi_build_icon_path(package, icon);
3726 index = MSI_RecordGetInteger(row,10);
3728 /* no value means 0 */
3729 if (index == MSI_NULL_INTEGER)
3730 index = 0;
3732 IShellLinkW_SetIconLocation(sl, path, index);
3733 msi_free(path);
3736 if (!MSI_RecordIsNull(row,11))
3737 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3739 if (!MSI_RecordIsNull(row,12))
3741 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3742 full_path = msi_get_target_folder( package, wkdir );
3743 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3745 link_file = get_link_file(package, row);
3747 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3748 IPersistFile_Save(pf, link_file, FALSE);
3749 msi_free(link_file);
3751 err:
3752 if (pf)
3753 IPersistFile_Release( pf );
3754 if (sl)
3755 IShellLinkW_Release( sl );
3757 return ERROR_SUCCESS;
3760 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3762 static const WCHAR query[] = {
3763 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3764 '`','S','h','o','r','t','c','u','t','`',0};
3765 MSIQUERY *view;
3766 HRESULT res;
3767 UINT rc;
3769 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3770 if (rc != ERROR_SUCCESS)
3771 return ERROR_SUCCESS;
3773 res = CoInitialize( NULL );
3775 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3776 msiobj_release(&view->hdr);
3778 if (SUCCEEDED(res)) CoUninitialize();
3779 return rc;
3782 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3784 MSIPACKAGE *package = param;
3785 LPWSTR link_file;
3786 LPCWSTR component;
3787 MSICOMPONENT *comp;
3789 component = MSI_RecordGetString( row, 4 );
3790 comp = msi_get_loaded_component( package, component );
3791 if (!comp)
3792 return ERROR_SUCCESS;
3794 comp->Action = msi_get_component_action( package, comp );
3795 if (comp->Action != INSTALLSTATE_ABSENT)
3797 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3798 return ERROR_SUCCESS;
3800 msi_ui_actiondata( package, szRemoveShortcuts, row );
3802 link_file = get_link_file( package, row );
3804 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3805 if (!DeleteFileW( link_file ))
3807 WARN("Failed to remove shortcut file %u\n", GetLastError());
3809 msi_free( link_file );
3811 return ERROR_SUCCESS;
3814 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3816 static const WCHAR query[] = {
3817 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3818 '`','S','h','o','r','t','c','u','t','`',0};
3819 MSIQUERY *view;
3820 UINT rc;
3822 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3823 if (rc != ERROR_SUCCESS)
3824 return ERROR_SUCCESS;
3826 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3827 msiobj_release( &view->hdr );
3828 return rc;
3831 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3833 MSIPACKAGE* package = param;
3834 HANDLE the_file;
3835 LPWSTR FilePath;
3836 LPCWSTR FileName;
3837 CHAR buffer[1024];
3838 DWORD sz;
3839 UINT rc;
3841 FileName = MSI_RecordGetString(row,1);
3842 if (!FileName)
3844 ERR("Unable to get FileName\n");
3845 return ERROR_SUCCESS;
3848 FilePath = msi_build_icon_path(package, FileName);
3850 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3852 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3853 FILE_ATTRIBUTE_NORMAL, NULL);
3855 if (the_file == INVALID_HANDLE_VALUE)
3857 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3858 msi_free(FilePath);
3859 return ERROR_SUCCESS;
3864 DWORD write;
3865 sz = 1024;
3866 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3867 if (rc != ERROR_SUCCESS)
3869 ERR("Failed to get stream\n");
3870 CloseHandle(the_file);
3871 DeleteFileW(FilePath);
3872 break;
3874 WriteFile(the_file,buffer,sz,&write,NULL);
3875 } while (sz == 1024);
3877 msi_free(FilePath);
3878 CloseHandle(the_file);
3880 return ERROR_SUCCESS;
3883 static UINT msi_publish_icons(MSIPACKAGE *package)
3885 static const WCHAR query[]= {
3886 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3887 '`','I','c','o','n','`',0};
3888 MSIQUERY *view;
3889 UINT r;
3891 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3892 if (r == ERROR_SUCCESS)
3894 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3895 msiobj_release(&view->hdr);
3896 if (r != ERROR_SUCCESS)
3897 return r;
3899 return ERROR_SUCCESS;
3902 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3904 UINT r;
3905 HKEY source;
3906 LPWSTR buffer;
3907 MSIMEDIADISK *disk;
3908 MSISOURCELISTINFO *info;
3910 r = RegCreateKeyW(hkey, szSourceList, &source);
3911 if (r != ERROR_SUCCESS)
3912 return r;
3914 RegCloseKey(source);
3916 buffer = strrchrW(package->PackagePath, '\\') + 1;
3917 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3918 package->Context, MSICODE_PRODUCT,
3919 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3920 if (r != ERROR_SUCCESS)
3921 return r;
3923 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3924 package->Context, MSICODE_PRODUCT,
3925 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3926 if (r != ERROR_SUCCESS)
3927 return r;
3929 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3930 package->Context, MSICODE_PRODUCT,
3931 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3932 if (r != ERROR_SUCCESS)
3933 return r;
3935 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3937 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3938 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3939 info->options, info->value);
3940 else
3941 MsiSourceListSetInfoW(package->ProductCode, NULL,
3942 info->context, info->options,
3943 info->property, info->value);
3946 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3948 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3949 disk->context, disk->options,
3950 disk->disk_id, disk->volume_label, disk->disk_prompt);
3953 return ERROR_SUCCESS;
3956 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3958 MSIHANDLE hdb, suminfo;
3959 WCHAR guids[MAX_PATH];
3960 WCHAR packcode[SQUISH_GUID_SIZE];
3961 LPWSTR buffer;
3962 LPWSTR ptr;
3963 DWORD langid;
3964 DWORD size;
3965 UINT r;
3967 static const WCHAR szARPProductIcon[] =
3968 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3969 static const WCHAR szAssignment[] =
3970 {'A','s','s','i','g','n','m','e','n','t',0};
3971 static const WCHAR szAdvertiseFlags[] =
3972 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3973 static const WCHAR szClients[] =
3974 {'C','l','i','e','n','t','s',0};
3975 static const WCHAR szColon[] = {':',0};
3977 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3978 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3979 msi_free(buffer);
3981 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3982 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3984 /* FIXME */
3985 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3987 buffer = msi_dup_property(package->db, szARPProductIcon);
3988 if (buffer)
3990 LPWSTR path = msi_build_icon_path(package, buffer);
3991 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3992 msi_free(path);
3993 msi_free(buffer);
3996 buffer = msi_dup_property(package->db, szProductVersion);
3997 if (buffer)
3999 DWORD verdword = msi_version_str_to_dword(buffer);
4000 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4001 msi_free(buffer);
4004 msi_reg_set_val_dword(hkey, szAssignment, 0);
4005 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4006 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4007 msi_reg_set_val_str(hkey, szClients, szColon);
4009 hdb = alloc_msihandle(&package->db->hdr);
4010 if (!hdb)
4011 return ERROR_NOT_ENOUGH_MEMORY;
4013 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4014 MsiCloseHandle(hdb);
4015 if (r != ERROR_SUCCESS)
4016 goto done;
4018 size = MAX_PATH;
4019 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4020 NULL, guids, &size);
4021 if (r != ERROR_SUCCESS)
4022 goto done;
4024 ptr = strchrW(guids, ';');
4025 if (ptr) *ptr = 0;
4026 squash_guid(guids, packcode);
4027 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4029 done:
4030 MsiCloseHandle(suminfo);
4031 return ERROR_SUCCESS;
4034 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4036 UINT r;
4037 HKEY hkey;
4038 LPWSTR upgrade;
4039 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4041 upgrade = msi_dup_property(package->db, szUpgradeCode);
4042 if (!upgrade)
4043 return ERROR_SUCCESS;
4045 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4046 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4047 else
4048 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4050 if (r != ERROR_SUCCESS)
4052 WARN("failed to open upgrade code key\n");
4053 msi_free(upgrade);
4054 return ERROR_SUCCESS;
4056 squash_guid(package->ProductCode, squashed_pc);
4057 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4058 RegCloseKey(hkey);
4059 msi_free(upgrade);
4060 return ERROR_SUCCESS;
4063 static BOOL msi_check_publish(MSIPACKAGE *package)
4065 MSIFEATURE *feature;
4067 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4069 feature->Action = msi_get_feature_action( package, feature );
4070 if (feature->Action == INSTALLSTATE_LOCAL)
4071 return TRUE;
4074 return FALSE;
4077 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4079 MSIFEATURE *feature;
4081 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4083 feature->Action = msi_get_feature_action( package, feature );
4084 if (feature->Action != INSTALLSTATE_ABSENT)
4085 return FALSE;
4088 return TRUE;
4091 static UINT msi_publish_patches( MSIPACKAGE *package )
4093 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4094 WCHAR patch_squashed[GUID_SIZE];
4095 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4096 LONG res;
4097 MSIPATCHINFO *patch;
4098 UINT r;
4099 WCHAR *p, *all_patches = NULL;
4100 DWORD len = 0;
4102 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4103 if (r != ERROR_SUCCESS)
4104 return ERROR_FUNCTION_FAILED;
4106 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4107 if (res != ERROR_SUCCESS)
4109 r = ERROR_FUNCTION_FAILED;
4110 goto done;
4113 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4114 if (r != ERROR_SUCCESS)
4115 goto done;
4117 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4119 squash_guid( patch->patchcode, patch_squashed );
4120 len += strlenW( patch_squashed ) + 1;
4123 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4124 if (!all_patches)
4125 goto done;
4127 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4129 HKEY patch_key;
4131 squash_guid( patch->patchcode, p );
4132 p += strlenW( p ) + 1;
4134 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4135 (const BYTE *)patch->transforms,
4136 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4137 if (res != ERROR_SUCCESS)
4138 goto done;
4140 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4141 if (r != ERROR_SUCCESS)
4142 goto done;
4144 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4145 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4146 RegCloseKey( patch_key );
4147 if (res != ERROR_SUCCESS)
4148 goto done;
4150 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4152 res = GetLastError();
4153 ERR("Unable to copy patch package %d\n", res);
4154 goto done;
4156 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4157 if (res != ERROR_SUCCESS)
4158 goto done;
4160 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4161 RegCloseKey( patch_key );
4162 if (res != ERROR_SUCCESS)
4163 goto done;
4166 all_patches[len] = 0;
4167 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4168 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4169 if (res != ERROR_SUCCESS)
4170 goto done;
4172 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4173 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4174 if (res != ERROR_SUCCESS)
4175 r = ERROR_FUNCTION_FAILED;
4177 done:
4178 RegCloseKey( product_patches_key );
4179 RegCloseKey( patches_key );
4180 RegCloseKey( product_key );
4181 msi_free( all_patches );
4182 return r;
4185 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4187 UINT rc;
4188 HKEY hukey = NULL, hudkey = NULL;
4189 MSIRECORD *uirow;
4191 if (!list_empty(&package->patches))
4193 rc = msi_publish_patches(package);
4194 if (rc != ERROR_SUCCESS)
4195 goto end;
4198 /* FIXME: also need to publish if the product is in advertise mode */
4199 if (!msi_check_publish(package))
4200 return ERROR_SUCCESS;
4202 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4203 &hukey, TRUE);
4204 if (rc != ERROR_SUCCESS)
4205 goto end;
4207 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4208 NULL, &hudkey, TRUE);
4209 if (rc != ERROR_SUCCESS)
4210 goto end;
4212 rc = msi_publish_upgrade_code(package);
4213 if (rc != ERROR_SUCCESS)
4214 goto end;
4216 rc = msi_publish_product_properties(package, hukey);
4217 if (rc != ERROR_SUCCESS)
4218 goto end;
4220 rc = msi_publish_sourcelist(package, hukey);
4221 if (rc != ERROR_SUCCESS)
4222 goto end;
4224 rc = msi_publish_icons(package);
4226 end:
4227 uirow = MSI_CreateRecord( 1 );
4228 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4229 msi_ui_actiondata( package, szPublishProduct, uirow );
4230 msiobj_release( &uirow->hdr );
4232 RegCloseKey(hukey);
4233 RegCloseKey(hudkey);
4234 return rc;
4237 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4239 WCHAR *filename, *ptr, *folder, *ret;
4240 const WCHAR *dirprop;
4242 filename = msi_dup_record_field( row, 2 );
4243 if (filename && (ptr = strchrW( filename, '|' )))
4244 ptr++;
4245 else
4246 ptr = filename;
4248 dirprop = MSI_RecordGetString( row, 3 );
4249 if (dirprop)
4251 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4252 if (!folder) folder = msi_dup_property( package->db, dirprop );
4254 else
4255 folder = msi_dup_property( package->db, szWindowsFolder );
4257 if (!folder)
4259 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4260 msi_free( filename );
4261 return NULL;
4264 ret = msi_build_directory_name( 2, folder, ptr );
4266 msi_free( filename );
4267 msi_free( folder );
4268 return ret;
4271 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4273 MSIPACKAGE *package = param;
4274 LPCWSTR component, section, key, value, identifier;
4275 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4276 MSIRECORD * uirow;
4277 INT action;
4278 MSICOMPONENT *comp;
4280 component = MSI_RecordGetString(row, 8);
4281 comp = msi_get_loaded_component(package,component);
4282 if (!comp)
4283 return ERROR_SUCCESS;
4285 comp->Action = msi_get_component_action( package, comp );
4286 if (comp->Action != INSTALLSTATE_LOCAL)
4288 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4289 return ERROR_SUCCESS;
4292 identifier = MSI_RecordGetString(row,1);
4293 section = MSI_RecordGetString(row,4);
4294 key = MSI_RecordGetString(row,5);
4295 value = MSI_RecordGetString(row,6);
4296 action = MSI_RecordGetInteger(row,7);
4298 deformat_string(package,section,&deformated_section);
4299 deformat_string(package,key,&deformated_key);
4300 deformat_string(package,value,&deformated_value);
4302 fullname = get_ini_file_name(package, row);
4304 if (action == 0)
4306 TRACE("Adding value %s to section %s in %s\n",
4307 debugstr_w(deformated_key), debugstr_w(deformated_section),
4308 debugstr_w(fullname));
4309 WritePrivateProfileStringW(deformated_section, deformated_key,
4310 deformated_value, fullname);
4312 else if (action == 1)
4314 WCHAR returned[10];
4315 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4316 returned, 10, fullname);
4317 if (returned[0] == 0)
4319 TRACE("Adding value %s to section %s in %s\n",
4320 debugstr_w(deformated_key), debugstr_w(deformated_section),
4321 debugstr_w(fullname));
4323 WritePrivateProfileStringW(deformated_section, deformated_key,
4324 deformated_value, fullname);
4327 else if (action == 3)
4328 FIXME("Append to existing section not yet implemented\n");
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, szWriteIniValues, uirow );
4336 msiobj_release( &uirow->hdr );
4338 msi_free(fullname);
4339 msi_free(deformated_key);
4340 msi_free(deformated_value);
4341 msi_free(deformated_section);
4342 return ERROR_SUCCESS;
4345 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4347 static const WCHAR query[] = {
4348 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4349 '`','I','n','i','F','i','l','e','`',0};
4350 MSIQUERY *view;
4351 UINT rc;
4353 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4354 if (rc != ERROR_SUCCESS)
4355 return ERROR_SUCCESS;
4357 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4358 msiobj_release(&view->hdr);
4359 return rc;
4362 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4364 MSIPACKAGE *package = param;
4365 LPCWSTR component, section, key, value, identifier;
4366 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4367 MSICOMPONENT *comp;
4368 MSIRECORD *uirow;
4369 INT action;
4371 component = MSI_RecordGetString( row, 8 );
4372 comp = msi_get_loaded_component( package, component );
4373 if (!comp)
4374 return ERROR_SUCCESS;
4376 comp->Action = msi_get_component_action( package, comp );
4377 if (comp->Action != INSTALLSTATE_ABSENT)
4379 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4380 return ERROR_SUCCESS;
4383 identifier = MSI_RecordGetString( row, 1 );
4384 section = MSI_RecordGetString( row, 4 );
4385 key = MSI_RecordGetString( row, 5 );
4386 value = MSI_RecordGetString( row, 6 );
4387 action = MSI_RecordGetInteger( row, 7 );
4389 deformat_string( package, section, &deformated_section );
4390 deformat_string( package, key, &deformated_key );
4391 deformat_string( package, value, &deformated_value );
4393 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4395 filename = get_ini_file_name( package, row );
4397 TRACE("Removing key %s from section %s in %s\n",
4398 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4400 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4402 WARN("Unable to remove key %u\n", GetLastError());
4404 msi_free( filename );
4406 else
4407 FIXME("Unsupported action %d\n", action);
4410 uirow = MSI_CreateRecord( 4 );
4411 MSI_RecordSetStringW( uirow, 1, identifier );
4412 MSI_RecordSetStringW( uirow, 2, deformated_section );
4413 MSI_RecordSetStringW( uirow, 3, deformated_key );
4414 MSI_RecordSetStringW( uirow, 4, deformated_value );
4415 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4416 msiobj_release( &uirow->hdr );
4418 msi_free( deformated_key );
4419 msi_free( deformated_value );
4420 msi_free( deformated_section );
4421 return ERROR_SUCCESS;
4424 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4426 MSIPACKAGE *package = param;
4427 LPCWSTR component, section, key, value, identifier;
4428 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4429 MSICOMPONENT *comp;
4430 MSIRECORD *uirow;
4431 INT action;
4433 component = MSI_RecordGetString( row, 8 );
4434 comp = msi_get_loaded_component( package, component );
4435 if (!comp)
4436 return ERROR_SUCCESS;
4438 comp->Action = msi_get_component_action( package, comp );
4439 if (comp->Action != INSTALLSTATE_LOCAL)
4441 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4442 return ERROR_SUCCESS;
4445 identifier = MSI_RecordGetString( row, 1 );
4446 section = MSI_RecordGetString( row, 4 );
4447 key = MSI_RecordGetString( row, 5 );
4448 value = MSI_RecordGetString( row, 6 );
4449 action = MSI_RecordGetInteger( row, 7 );
4451 deformat_string( package, section, &deformated_section );
4452 deformat_string( package, key, &deformated_key );
4453 deformat_string( package, value, &deformated_value );
4455 if (action == msidbIniFileActionRemoveLine)
4457 filename = get_ini_file_name( package, row );
4459 TRACE("Removing key %s from section %s in %s\n",
4460 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4462 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4464 WARN("Unable to remove key %u\n", GetLastError());
4466 msi_free( filename );
4468 else
4469 FIXME("Unsupported action %d\n", action);
4471 uirow = MSI_CreateRecord( 4 );
4472 MSI_RecordSetStringW( uirow, 1, identifier );
4473 MSI_RecordSetStringW( uirow, 2, deformated_section );
4474 MSI_RecordSetStringW( uirow, 3, deformated_key );
4475 MSI_RecordSetStringW( uirow, 4, deformated_value );
4476 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4477 msiobj_release( &uirow->hdr );
4479 msi_free( deformated_key );
4480 msi_free( deformated_value );
4481 msi_free( deformated_section );
4482 return ERROR_SUCCESS;
4485 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4487 static const WCHAR query[] = {
4488 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4489 '`','I','n','i','F','i','l','e','`',0};
4490 static const WCHAR remove_query[] = {
4491 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4492 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4493 MSIQUERY *view;
4494 UINT rc;
4496 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4497 if (rc == ERROR_SUCCESS)
4499 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4500 msiobj_release( &view->hdr );
4501 if (rc != ERROR_SUCCESS)
4502 return rc;
4504 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4505 if (rc == ERROR_SUCCESS)
4507 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4508 msiobj_release( &view->hdr );
4509 if (rc != ERROR_SUCCESS)
4510 return rc;
4512 return ERROR_SUCCESS;
4515 static void register_dll( const WCHAR *dll, BOOL unregister )
4517 HMODULE hmod;
4519 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4520 if (hmod)
4522 HRESULT (WINAPI *func_ptr)( void );
4523 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4525 func_ptr = (void *)GetProcAddress( hmod, func );
4526 if (func_ptr)
4528 HRESULT hr = func_ptr();
4529 if (FAILED( hr ))
4530 WARN("failed to register dll 0x%08x\n", hr);
4532 else
4533 WARN("entry point %s not found\n", func);
4534 FreeLibrary( hmod );
4535 return;
4537 WARN("failed to load library %u\n", GetLastError());
4540 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4542 MSIPACKAGE *package = param;
4543 LPCWSTR filename;
4544 MSIFILE *file;
4545 MSIRECORD *uirow;
4547 filename = MSI_RecordGetString( row, 1 );
4548 file = msi_get_loaded_file( package, filename );
4549 if (!file)
4551 WARN("unable to find file %s\n", debugstr_w(filename));
4552 return ERROR_SUCCESS;
4554 file->Component->Action = msi_get_component_action( package, file->Component );
4555 if (file->Component->Action != INSTALLSTATE_LOCAL)
4557 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4558 return ERROR_SUCCESS;
4561 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4562 register_dll( file->TargetPath, FALSE );
4564 uirow = MSI_CreateRecord( 2 );
4565 MSI_RecordSetStringW( uirow, 1, file->File );
4566 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4567 msi_ui_actiondata( package, szSelfRegModules, uirow );
4568 msiobj_release( &uirow->hdr );
4570 return ERROR_SUCCESS;
4573 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4575 static const WCHAR query[] = {
4576 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4577 '`','S','e','l','f','R','e','g','`',0};
4578 MSIQUERY *view;
4579 UINT rc;
4581 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4582 if (rc != ERROR_SUCCESS)
4583 return ERROR_SUCCESS;
4585 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4586 msiobj_release(&view->hdr);
4587 return rc;
4590 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4592 MSIPACKAGE *package = param;
4593 LPCWSTR filename;
4594 MSIFILE *file;
4595 MSIRECORD *uirow;
4597 filename = MSI_RecordGetString( row, 1 );
4598 file = msi_get_loaded_file( package, filename );
4599 if (!file)
4601 WARN("unable to find file %s\n", debugstr_w(filename));
4602 return ERROR_SUCCESS;
4604 file->Component->Action = msi_get_component_action( package, file->Component );
4605 if (file->Component->Action != INSTALLSTATE_ABSENT)
4607 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4608 return ERROR_SUCCESS;
4611 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4612 register_dll( file->TargetPath, TRUE );
4614 uirow = MSI_CreateRecord( 2 );
4615 MSI_RecordSetStringW( uirow, 1, file->File );
4616 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4617 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4618 msiobj_release( &uirow->hdr );
4620 return ERROR_SUCCESS;
4623 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4625 static const WCHAR query[] = {
4626 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4627 '`','S','e','l','f','R','e','g','`',0};
4628 MSIQUERY *view;
4629 UINT rc;
4631 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4632 if (rc != ERROR_SUCCESS)
4633 return ERROR_SUCCESS;
4635 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4636 msiobj_release( &view->hdr );
4637 return rc;
4640 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4642 MSIFEATURE *feature;
4643 UINT rc;
4644 HKEY hkey = NULL, userdata = NULL;
4646 if (!msi_check_publish(package))
4647 return ERROR_SUCCESS;
4649 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4650 &hkey, TRUE);
4651 if (rc != ERROR_SUCCESS)
4652 goto end;
4654 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4655 &userdata, TRUE);
4656 if (rc != ERROR_SUCCESS)
4657 goto end;
4659 /* here the guids are base 85 encoded */
4660 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4662 ComponentList *cl;
4663 LPWSTR data = NULL;
4664 GUID clsid;
4665 INT size;
4666 BOOL absent = FALSE;
4667 MSIRECORD *uirow;
4669 if (feature->Action != INSTALLSTATE_LOCAL &&
4670 feature->Action != INSTALLSTATE_SOURCE &&
4671 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4673 size = 1;
4674 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4676 size += 21;
4678 if (feature->Feature_Parent)
4679 size += strlenW( feature->Feature_Parent )+2;
4681 data = msi_alloc(size * sizeof(WCHAR));
4683 data[0] = 0;
4684 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4686 MSICOMPONENT* component = cl->component;
4687 WCHAR buf[21];
4689 buf[0] = 0;
4690 if (component->ComponentId)
4692 TRACE("From %s\n",debugstr_w(component->ComponentId));
4693 CLSIDFromString(component->ComponentId, &clsid);
4694 encode_base85_guid(&clsid,buf);
4695 TRACE("to %s\n",debugstr_w(buf));
4696 strcatW(data,buf);
4700 if (feature->Feature_Parent)
4702 static const WCHAR sep[] = {'\2',0};
4703 strcatW(data,sep);
4704 strcatW(data,feature->Feature_Parent);
4707 msi_reg_set_val_str( userdata, feature->Feature, data );
4708 msi_free(data);
4710 size = 0;
4711 if (feature->Feature_Parent)
4712 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4713 if (!absent)
4715 size += sizeof(WCHAR);
4716 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4717 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4719 else
4721 size += 2*sizeof(WCHAR);
4722 data = msi_alloc(size);
4723 data[0] = 0x6;
4724 data[1] = 0;
4725 if (feature->Feature_Parent)
4726 strcpyW( &data[1], feature->Feature_Parent );
4727 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4728 (LPBYTE)data,size);
4729 msi_free(data);
4732 /* the UI chunk */
4733 uirow = MSI_CreateRecord( 1 );
4734 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4735 msi_ui_actiondata( package, szPublishFeatures, uirow );
4736 msiobj_release( &uirow->hdr );
4737 /* FIXME: call msi_ui_progress? */
4740 end:
4741 RegCloseKey(hkey);
4742 RegCloseKey(userdata);
4743 return rc;
4746 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4748 UINT r;
4749 HKEY hkey;
4750 MSIRECORD *uirow;
4752 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4754 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4755 &hkey, FALSE);
4756 if (r == ERROR_SUCCESS)
4758 RegDeleteValueW(hkey, feature->Feature);
4759 RegCloseKey(hkey);
4762 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4763 &hkey, FALSE);
4764 if (r == ERROR_SUCCESS)
4766 RegDeleteValueW(hkey, feature->Feature);
4767 RegCloseKey(hkey);
4770 uirow = MSI_CreateRecord( 1 );
4771 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4772 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4773 msiobj_release( &uirow->hdr );
4775 return ERROR_SUCCESS;
4778 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4780 MSIFEATURE *feature;
4782 if (!msi_check_unpublish(package))
4783 return ERROR_SUCCESS;
4785 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4787 msi_unpublish_feature(package, feature);
4790 return ERROR_SUCCESS;
4793 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4795 SYSTEMTIME systime;
4796 DWORD size, langid;
4797 WCHAR date[9], *val, *buffer;
4798 const WCHAR *prop, *key;
4800 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4801 static const WCHAR modpath_fmt[] =
4802 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4803 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4804 static const WCHAR szModifyPath[] =
4805 {'M','o','d','i','f','y','P','a','t','h',0};
4806 static const WCHAR szUninstallString[] =
4807 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4808 static const WCHAR szEstimatedSize[] =
4809 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4810 static const WCHAR szDisplayVersion[] =
4811 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4812 static const WCHAR szInstallSource[] =
4813 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4814 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4815 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4816 static const WCHAR szAuthorizedCDFPrefix[] =
4817 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4818 static const WCHAR szARPCONTACT[] =
4819 {'A','R','P','C','O','N','T','A','C','T',0};
4820 static const WCHAR szContact[] =
4821 {'C','o','n','t','a','c','t',0};
4822 static const WCHAR szARPCOMMENTS[] =
4823 {'A','R','P','C','O','M','M','E','N','T','S',0};
4824 static const WCHAR szComments[] =
4825 {'C','o','m','m','e','n','t','s',0};
4826 static const WCHAR szProductName[] =
4827 {'P','r','o','d','u','c','t','N','a','m','e',0};
4828 static const WCHAR szDisplayName[] =
4829 {'D','i','s','p','l','a','y','N','a','m','e',0};
4830 static const WCHAR szARPHELPLINK[] =
4831 {'A','R','P','H','E','L','P','L','I','N','K',0};
4832 static const WCHAR szHelpLink[] =
4833 {'H','e','l','p','L','i','n','k',0};
4834 static const WCHAR szARPHELPTELEPHONE[] =
4835 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4836 static const WCHAR szHelpTelephone[] =
4837 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4838 static const WCHAR szARPINSTALLLOCATION[] =
4839 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4840 static const WCHAR szInstallLocation[] =
4841 {'I','n','s','t','a','l','l','L','o','c','a','t','i','o','n',0};
4842 static const WCHAR szManufacturer[] =
4843 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4844 static const WCHAR szPublisher[] =
4845 {'P','u','b','l','i','s','h','e','r',0};
4846 static const WCHAR szARPREADME[] =
4847 {'A','R','P','R','E','A','D','M','E',0};
4848 static const WCHAR szReadme[] =
4849 {'R','e','a','d','M','e',0};
4850 static const WCHAR szARPSIZE[] =
4851 {'A','R','P','S','I','Z','E',0};
4852 static const WCHAR szSize[] =
4853 {'S','i','z','e',0};
4854 static const WCHAR szARPURLINFOABOUT[] =
4855 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4856 static const WCHAR szURLInfoAbout[] =
4857 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4858 static const WCHAR szARPURLUPDATEINFO[] =
4859 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4860 static const WCHAR szURLUpdateInfo[] =
4861 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4862 static const WCHAR szARPSYSTEMCOMPONENT[] =
4863 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4864 static const WCHAR szSystemComponent[] =
4865 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4867 static const WCHAR *propval[] = {
4868 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4869 szARPCONTACT, szContact,
4870 szARPCOMMENTS, szComments,
4871 szProductName, szDisplayName,
4872 szARPHELPLINK, szHelpLink,
4873 szARPHELPTELEPHONE, szHelpTelephone,
4874 szARPINSTALLLOCATION, szInstallLocation,
4875 szSourceDir, szInstallSource,
4876 szManufacturer, szPublisher,
4877 szARPREADME, szReadme,
4878 szARPSIZE, szSize,
4879 szARPURLINFOABOUT, szURLInfoAbout,
4880 szARPURLUPDATEINFO, szURLUpdateInfo,
4881 NULL
4883 const WCHAR **p = propval;
4885 while (*p)
4887 prop = *p++;
4888 key = *p++;
4889 val = msi_dup_property(package->db, prop);
4890 msi_reg_set_val_str(hkey, key, val);
4891 msi_free(val);
4894 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4895 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4897 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4899 size = deformat_string(package, modpath_fmt, &buffer);
4900 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4901 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4902 msi_free(buffer);
4904 /* FIXME: Write real Estimated Size when we have it */
4905 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4907 GetLocalTime(&systime);
4908 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4909 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4911 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4912 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4914 buffer = msi_dup_property(package->db, szProductVersion);
4915 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4916 if (buffer)
4918 DWORD verdword = msi_version_str_to_dword(buffer);
4920 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4921 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4922 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4923 msi_free(buffer);
4926 return ERROR_SUCCESS;
4929 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4931 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4932 MSIRECORD *uirow;
4933 LPWSTR upgrade_code;
4934 HKEY hkey, props, upgrade_key;
4935 UINT rc;
4937 /* FIXME: also need to publish if the product is in advertise mode */
4938 if (!msi_check_publish(package))
4939 return ERROR_SUCCESS;
4941 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4942 if (rc != ERROR_SUCCESS)
4943 return rc;
4945 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4946 if (rc != ERROR_SUCCESS)
4947 goto done;
4949 rc = msi_publish_install_properties(package, hkey);
4950 if (rc != ERROR_SUCCESS)
4951 goto done;
4953 rc = msi_publish_install_properties(package, props);
4954 if (rc != ERROR_SUCCESS)
4955 goto done;
4957 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4958 if (upgrade_code)
4960 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4961 if (rc == ERROR_SUCCESS)
4963 squash_guid( package->ProductCode, squashed_pc );
4964 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4965 RegCloseKey( upgrade_key );
4967 msi_free( upgrade_code );
4969 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4970 package->delete_on_close = FALSE;
4972 done:
4973 uirow = MSI_CreateRecord( 1 );
4974 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4975 msi_ui_actiondata( package, szRegisterProduct, uirow );
4976 msiobj_release( &uirow->hdr );
4978 RegCloseKey(hkey);
4979 return ERROR_SUCCESS;
4982 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4984 return execute_script(package, SCRIPT_INSTALL);
4987 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
4989 MSIPACKAGE *package = param;
4990 const WCHAR *icon = MSI_RecordGetString( row, 1 );
4991 WCHAR *p, *icon_path;
4993 if (!icon) return ERROR_SUCCESS;
4994 if ((icon_path = msi_build_icon_path( package, icon )))
4996 TRACE("removing icon file %s\n", debugstr_w(icon_path));
4997 DeleteFileW( icon_path );
4998 if ((p = strrchrW( icon_path, '\\' )))
5000 *p = 0;
5001 RemoveDirectoryW( icon_path );
5003 msi_free( icon_path );
5005 return ERROR_SUCCESS;
5008 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5010 static const WCHAR query[]= {
5011 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5012 MSIQUERY *view;
5013 UINT r;
5015 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5016 if (r == ERROR_SUCCESS)
5018 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5019 msiobj_release( &view->hdr );
5020 if (r != ERROR_SUCCESS)
5021 return r;
5023 return ERROR_SUCCESS;
5026 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5028 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5029 WCHAR *upgrade, **features;
5030 BOOL full_uninstall = TRUE;
5031 MSIFEATURE *feature;
5032 MSIPATCHINFO *patch;
5033 UINT i;
5035 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5037 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5039 features = msi_split_string( remove, ',' );
5040 for (i = 0; features && features[i]; i++)
5042 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5044 msi_free(features);
5046 if (!full_uninstall)
5047 return ERROR_SUCCESS;
5049 MSIREG_DeleteProductKey(package->ProductCode);
5050 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5051 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5053 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5054 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5055 MSIREG_DeleteUserProductKey(package->ProductCode);
5056 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5058 upgrade = msi_dup_property(package->db, szUpgradeCode);
5059 if (upgrade)
5061 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5062 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5063 msi_free(upgrade);
5066 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5068 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5069 if (!strcmpW( package->ProductCode, patch->products ))
5071 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5072 patch->delete_on_close = TRUE;
5074 /* FIXME: remove local patch package if this is the last product */
5076 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5077 package->delete_on_close = TRUE;
5079 msi_unpublish_icons( package );
5080 return ERROR_SUCCESS;
5083 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5085 UINT rc;
5086 WCHAR *remove;
5088 /* turn off scheduling */
5089 package->script->CurrentlyScripting= FALSE;
5091 /* first do the same as an InstallExecute */
5092 rc = ACTION_InstallExecute(package);
5093 if (rc != ERROR_SUCCESS)
5094 return rc;
5096 /* then handle commit actions */
5097 rc = execute_script(package, SCRIPT_COMMIT);
5098 if (rc != ERROR_SUCCESS)
5099 return rc;
5101 remove = msi_dup_property(package->db, szRemove);
5102 rc = msi_unpublish_product(package, remove);
5103 msi_free(remove);
5104 return rc;
5107 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5109 static const WCHAR RunOnce[] = {
5110 'S','o','f','t','w','a','r','e','\\',
5111 'M','i','c','r','o','s','o','f','t','\\',
5112 'W','i','n','d','o','w','s','\\',
5113 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5114 'R','u','n','O','n','c','e',0};
5115 static const WCHAR InstallRunOnce[] = {
5116 'S','o','f','t','w','a','r','e','\\',
5117 'M','i','c','r','o','s','o','f','t','\\',
5118 'W','i','n','d','o','w','s','\\',
5119 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5120 'I','n','s','t','a','l','l','e','r','\\',
5121 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5123 static const WCHAR msiexec_fmt[] = {
5124 '%','s',
5125 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5126 '\"','%','s','\"',0};
5127 static const WCHAR install_fmt[] = {
5128 '/','I',' ','\"','%','s','\"',' ',
5129 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5130 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5131 WCHAR buffer[256], sysdir[MAX_PATH];
5132 HKEY hkey;
5133 WCHAR squished_pc[100];
5135 squash_guid(package->ProductCode,squished_pc);
5137 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5138 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5139 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5140 squished_pc);
5142 msi_reg_set_val_str( hkey, squished_pc, buffer );
5143 RegCloseKey(hkey);
5145 TRACE("Reboot command %s\n",debugstr_w(buffer));
5147 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5148 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5150 msi_reg_set_val_str( hkey, squished_pc, buffer );
5151 RegCloseKey(hkey);
5153 return ERROR_INSTALL_SUSPEND;
5156 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5158 static const WCHAR query[] =
5159 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5160 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5161 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5162 MSIRECORD *rec, *row;
5163 DWORD i, size = 0;
5164 va_list va;
5165 const WCHAR *str;
5166 WCHAR *data;
5168 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5170 rec = MSI_CreateRecord( count + 2 );
5171 str = MSI_RecordGetString( row, 1 );
5172 MSI_RecordSetStringW( rec, 0, str );
5173 msiobj_release( &row->hdr );
5174 MSI_RecordSetInteger( rec, 1, error );
5176 va_start( va, count );
5177 for (i = 0; i < count; i++)
5179 str = va_arg( va, const WCHAR *);
5180 MSI_RecordSetStringW( rec, i + 2, str );
5182 va_end( va );
5184 MSI_FormatRecordW( package, rec, NULL, &size );
5185 size++;
5186 data = msi_alloc( size * sizeof(WCHAR) );
5187 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5188 else data[0] = 0;
5189 msiobj_release( &rec->hdr );
5190 return data;
5193 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5195 DWORD attrib;
5196 UINT rc;
5199 * We are currently doing what should be done here in the top level Install
5200 * however for Administrative and uninstalls this step will be needed
5202 if (!package->PackagePath)
5203 return ERROR_SUCCESS;
5205 msi_set_sourcedir_props(package, TRUE);
5207 attrib = GetFileAttributesW(package->db->path);
5208 if (attrib == INVALID_FILE_ATTRIBUTES)
5210 LPWSTR prompt, msg;
5211 DWORD size = 0;
5213 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5214 package->Context, MSICODE_PRODUCT,
5215 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5216 if (rc == ERROR_MORE_DATA)
5218 prompt = msi_alloc(size * sizeof(WCHAR));
5219 MsiSourceListGetInfoW(package->ProductCode, NULL,
5220 package->Context, MSICODE_PRODUCT,
5221 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5223 else
5224 prompt = strdupW(package->db->path);
5226 msg = msi_build_error_string(package, 1302, 1, prompt);
5227 msi_free(prompt);
5228 while(attrib == INVALID_FILE_ATTRIBUTES)
5230 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5231 if (rc == IDCANCEL)
5233 msi_free(msg);
5234 return ERROR_INSTALL_USEREXIT;
5236 attrib = GetFileAttributesW(package->db->path);
5238 msi_free(msg);
5239 rc = ERROR_SUCCESS;
5241 else
5242 return ERROR_SUCCESS;
5244 return rc;
5247 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5249 HKEY hkey = 0;
5250 LPWSTR buffer, productid = NULL;
5251 UINT i, rc = ERROR_SUCCESS;
5252 MSIRECORD *uirow;
5254 static const WCHAR szPropKeys[][80] =
5256 {'P','r','o','d','u','c','t','I','D',0},
5257 {'U','S','E','R','N','A','M','E',0},
5258 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5259 {0},
5262 static const WCHAR szRegKeys[][80] =
5264 {'P','r','o','d','u','c','t','I','D',0},
5265 {'R','e','g','O','w','n','e','r',0},
5266 {'R','e','g','C','o','m','p','a','n','y',0},
5267 {0},
5270 if (msi_check_unpublish(package))
5272 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5273 goto end;
5276 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5277 if (!productid)
5278 goto end;
5280 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5281 NULL, &hkey, TRUE);
5282 if (rc != ERROR_SUCCESS)
5283 goto end;
5285 for( i = 0; szPropKeys[i][0]; i++ )
5287 buffer = msi_dup_property( package->db, szPropKeys[i] );
5288 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5289 msi_free( buffer );
5292 end:
5293 uirow = MSI_CreateRecord( 1 );
5294 MSI_RecordSetStringW( uirow, 1, productid );
5295 msi_ui_actiondata( package, szRegisterUser, uirow );
5296 msiobj_release( &uirow->hdr );
5298 msi_free(productid);
5299 RegCloseKey(hkey);
5300 return rc;
5304 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5306 UINT rc;
5308 package->script->InWhatSequence |= SEQUENCE_EXEC;
5309 rc = ACTION_ProcessExecSequence(package,FALSE);
5310 return rc;
5313 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5315 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5316 WCHAR productid_85[21], component_85[21], *ret;
5317 GUID clsid;
5318 DWORD sz;
5320 /* > is used if there is a component GUID and < if not. */
5322 productid_85[0] = 0;
5323 component_85[0] = 0;
5324 CLSIDFromString( package->ProductCode, &clsid );
5326 encode_base85_guid( &clsid, productid_85 );
5327 if (component)
5329 CLSIDFromString( component->ComponentId, &clsid );
5330 encode_base85_guid( &clsid, component_85 );
5333 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5334 debugstr_w(component_85));
5336 sz = 20 + strlenW( feature ) + 20 + 3;
5337 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5338 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5339 return ret;
5342 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5344 MSIPACKAGE *package = param;
5345 LPCWSTR compgroupid, component, feature, qualifier, text;
5346 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5347 HKEY hkey = NULL;
5348 UINT rc;
5349 MSICOMPONENT *comp;
5350 MSIFEATURE *feat;
5351 DWORD sz;
5352 MSIRECORD *uirow;
5353 int len;
5355 feature = MSI_RecordGetString(rec, 5);
5356 feat = msi_get_loaded_feature(package, feature);
5357 if (!feat)
5358 return ERROR_SUCCESS;
5360 feat->Action = msi_get_feature_action( package, feat );
5361 if (feat->Action != INSTALLSTATE_LOCAL &&
5362 feat->Action != INSTALLSTATE_SOURCE &&
5363 feat->Action != INSTALLSTATE_ADVERTISED)
5365 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5366 return ERROR_SUCCESS;
5369 component = MSI_RecordGetString(rec, 3);
5370 comp = msi_get_loaded_component(package, component);
5371 if (!comp)
5372 return ERROR_SUCCESS;
5374 compgroupid = MSI_RecordGetString(rec,1);
5375 qualifier = MSI_RecordGetString(rec,2);
5377 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5378 if (rc != ERROR_SUCCESS)
5379 goto end;
5381 advertise = msi_create_component_advertise_string( package, comp, feature );
5382 text = MSI_RecordGetString( rec, 4 );
5383 if (text)
5385 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5386 strcpyW( p, advertise );
5387 strcatW( p, text );
5388 msi_free( advertise );
5389 advertise = p;
5391 existing = msi_reg_get_val_str( hkey, qualifier );
5393 sz = strlenW( advertise ) + 1;
5394 if (existing)
5396 for (p = existing; *p; p += len)
5398 len = strlenW( p ) + 1;
5399 if (strcmpW( advertise, p )) sz += len;
5402 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5404 rc = ERROR_OUTOFMEMORY;
5405 goto end;
5407 q = output;
5408 if (existing)
5410 for (p = existing; *p; p += len)
5412 len = strlenW( p ) + 1;
5413 if (strcmpW( advertise, p ))
5415 memcpy( q, p, len * sizeof(WCHAR) );
5416 q += len;
5420 strcpyW( q, advertise );
5421 q[strlenW( q ) + 1] = 0;
5423 msi_reg_set_val_multi_str( hkey, qualifier, output );
5425 end:
5426 RegCloseKey(hkey);
5427 msi_free( output );
5428 msi_free( advertise );
5429 msi_free( existing );
5431 /* the UI chunk */
5432 uirow = MSI_CreateRecord( 2 );
5433 MSI_RecordSetStringW( uirow, 1, compgroupid );
5434 MSI_RecordSetStringW( uirow, 2, qualifier);
5435 msi_ui_actiondata( package, szPublishComponents, uirow );
5436 msiobj_release( &uirow->hdr );
5437 /* FIXME: call ui_progress? */
5439 return rc;
5443 * At present I am ignorning the advertised components part of this and only
5444 * focusing on the qualified component sets
5446 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5448 static const WCHAR query[] = {
5449 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5450 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5451 MSIQUERY *view;
5452 UINT rc;
5454 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5455 if (rc != ERROR_SUCCESS)
5456 return ERROR_SUCCESS;
5458 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5459 msiobj_release(&view->hdr);
5460 return rc;
5463 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5465 static const WCHAR szInstallerComponents[] = {
5466 'S','o','f','t','w','a','r','e','\\',
5467 'M','i','c','r','o','s','o','f','t','\\',
5468 'I','n','s','t','a','l','l','e','r','\\',
5469 'C','o','m','p','o','n','e','n','t','s','\\',0};
5471 MSIPACKAGE *package = param;
5472 LPCWSTR compgroupid, component, feature, qualifier;
5473 MSICOMPONENT *comp;
5474 MSIFEATURE *feat;
5475 MSIRECORD *uirow;
5476 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5477 LONG res;
5479 feature = MSI_RecordGetString( rec, 5 );
5480 feat = msi_get_loaded_feature( package, feature );
5481 if (!feat)
5482 return ERROR_SUCCESS;
5484 feat->Action = msi_get_feature_action( package, feat );
5485 if (feat->Action != INSTALLSTATE_ABSENT)
5487 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5488 return ERROR_SUCCESS;
5491 component = MSI_RecordGetString( rec, 3 );
5492 comp = msi_get_loaded_component( package, component );
5493 if (!comp)
5494 return ERROR_SUCCESS;
5496 compgroupid = MSI_RecordGetString( rec, 1 );
5497 qualifier = MSI_RecordGetString( rec, 2 );
5499 squash_guid( compgroupid, squashed );
5500 strcpyW( keypath, szInstallerComponents );
5501 strcatW( keypath, squashed );
5503 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5504 if (res != ERROR_SUCCESS)
5506 WARN("Unable to delete component key %d\n", res);
5509 uirow = MSI_CreateRecord( 2 );
5510 MSI_RecordSetStringW( uirow, 1, compgroupid );
5511 MSI_RecordSetStringW( uirow, 2, qualifier );
5512 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5513 msiobj_release( &uirow->hdr );
5515 return ERROR_SUCCESS;
5518 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5520 static const WCHAR query[] = {
5521 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5522 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5523 MSIQUERY *view;
5524 UINT rc;
5526 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5527 if (rc != ERROR_SUCCESS)
5528 return ERROR_SUCCESS;
5530 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5531 msiobj_release( &view->hdr );
5532 return rc;
5535 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5537 static const WCHAR query[] =
5538 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5539 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5540 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5541 MSIPACKAGE *package = param;
5542 MSICOMPONENT *component;
5543 MSIRECORD *row;
5544 MSIFILE *file;
5545 SC_HANDLE hscm = NULL, service = NULL;
5546 LPCWSTR comp, key;
5547 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5548 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5549 DWORD serv_type, start_type, err_control;
5550 SERVICE_DESCRIPTIONW sd = {NULL};
5552 comp = MSI_RecordGetString( rec, 12 );
5553 component = msi_get_loaded_component( package, comp );
5554 if (!component)
5556 WARN("service component not found\n");
5557 goto done;
5559 component->Action = msi_get_component_action( package, component );
5560 if (component->Action != INSTALLSTATE_LOCAL)
5562 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5563 goto done;
5565 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5566 if (!hscm)
5568 ERR("Failed to open the SC Manager!\n");
5569 goto done;
5572 start_type = MSI_RecordGetInteger(rec, 5);
5573 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5574 goto done;
5576 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5577 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5578 serv_type = MSI_RecordGetInteger(rec, 4);
5579 err_control = MSI_RecordGetInteger(rec, 6);
5580 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5581 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5582 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5583 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5584 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5585 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5587 /* fetch the service path */
5588 row = MSI_QueryGetRecord(package->db, query, comp);
5589 if (!row)
5591 ERR("Query failed\n");
5592 goto done;
5594 key = MSI_RecordGetString(row, 6);
5595 file = msi_get_loaded_file(package, key);
5596 msiobj_release(&row->hdr);
5597 if (!file)
5599 ERR("Failed to load the service file\n");
5600 goto done;
5603 if (!args || !args[0]) image_path = file->TargetPath;
5604 else
5606 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5607 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5608 return ERROR_OUTOFMEMORY;
5610 strcpyW(image_path, file->TargetPath);
5611 strcatW(image_path, szSpace);
5612 strcatW(image_path, args);
5614 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5615 start_type, err_control, image_path, load_order,
5616 NULL, depends, serv_name, pass);
5618 if (!service)
5620 if (GetLastError() != ERROR_SERVICE_EXISTS)
5621 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5623 else if (sd.lpDescription)
5625 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5626 WARN("failed to set service description %u\n", GetLastError());
5629 if (image_path != file->TargetPath) msi_free(image_path);
5630 done:
5631 CloseServiceHandle(service);
5632 CloseServiceHandle(hscm);
5633 msi_free(name);
5634 msi_free(disp);
5635 msi_free(sd.lpDescription);
5636 msi_free(load_order);
5637 msi_free(serv_name);
5638 msi_free(pass);
5639 msi_free(depends);
5640 msi_free(args);
5642 return ERROR_SUCCESS;
5645 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5647 static const WCHAR query[] = {
5648 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5649 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5650 MSIQUERY *view;
5651 UINT rc;
5653 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5654 if (rc != ERROR_SUCCESS)
5655 return ERROR_SUCCESS;
5657 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5658 msiobj_release(&view->hdr);
5659 return rc;
5662 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5663 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5665 LPCWSTR *vector, *temp_vector;
5666 LPWSTR p, q;
5667 DWORD sep_len;
5669 static const WCHAR separator[] = {'[','~',']',0};
5671 *numargs = 0;
5672 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5674 if (!args)
5675 return NULL;
5677 vector = msi_alloc(sizeof(LPWSTR));
5678 if (!vector)
5679 return NULL;
5681 p = args;
5684 (*numargs)++;
5685 vector[*numargs - 1] = p;
5687 if ((q = strstrW(p, separator)))
5689 *q = '\0';
5691 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5692 if (!temp_vector)
5694 msi_free(vector);
5695 return NULL;
5697 vector = temp_vector;
5699 p = q + sep_len;
5701 } while (q);
5703 return vector;
5706 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5708 MSIPACKAGE *package = param;
5709 MSICOMPONENT *comp;
5710 MSIRECORD *uirow;
5711 SC_HANDLE scm = NULL, service = NULL;
5712 LPCWSTR component, *vector = NULL;
5713 LPWSTR name, args, display_name = NULL;
5714 DWORD event, numargs, len, wait, dummy;
5715 UINT r = ERROR_FUNCTION_FAILED;
5716 SERVICE_STATUS_PROCESS status;
5717 ULONGLONG start_time;
5719 component = MSI_RecordGetString(rec, 6);
5720 comp = msi_get_loaded_component(package, component);
5721 if (!comp)
5722 return ERROR_SUCCESS;
5724 comp->Action = msi_get_component_action( package, comp );
5725 if (comp->Action != INSTALLSTATE_LOCAL)
5727 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5728 return ERROR_SUCCESS;
5731 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5732 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5733 event = MSI_RecordGetInteger(rec, 3);
5734 wait = MSI_RecordGetInteger(rec, 5);
5736 if (!(event & msidbServiceControlEventStart))
5738 r = ERROR_SUCCESS;
5739 goto done;
5742 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5743 if (!scm)
5745 ERR("Failed to open the service control manager\n");
5746 goto done;
5749 len = 0;
5750 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5751 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5753 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5754 GetServiceDisplayNameW( scm, name, display_name, &len );
5757 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5758 if (!service)
5760 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5761 goto done;
5764 vector = msi_service_args_to_vector(args, &numargs);
5766 if (!StartServiceW(service, numargs, vector) &&
5767 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5769 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5770 goto done;
5773 r = ERROR_SUCCESS;
5774 if (wait)
5776 /* wait for at most 30 seconds for the service to be up and running */
5777 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5778 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5780 TRACE("failed to query service status (%u)\n", GetLastError());
5781 goto done;
5783 start_time = GetTickCount64();
5784 while (status.dwCurrentState == SERVICE_START_PENDING)
5786 if (GetTickCount64() - start_time > 30000) break;
5787 Sleep(1000);
5788 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5789 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5791 TRACE("failed to query service status (%u)\n", GetLastError());
5792 goto done;
5795 if (status.dwCurrentState != SERVICE_RUNNING)
5797 WARN("service failed to start %u\n", status.dwCurrentState);
5798 r = ERROR_FUNCTION_FAILED;
5802 done:
5803 uirow = MSI_CreateRecord( 2 );
5804 MSI_RecordSetStringW( uirow, 1, display_name );
5805 MSI_RecordSetStringW( uirow, 2, name );
5806 msi_ui_actiondata( package, szStartServices, uirow );
5807 msiobj_release( &uirow->hdr );
5809 CloseServiceHandle(service);
5810 CloseServiceHandle(scm);
5812 msi_free(name);
5813 msi_free(args);
5814 msi_free(vector);
5815 msi_free(display_name);
5816 return r;
5819 static UINT ACTION_StartServices( MSIPACKAGE *package )
5821 static const WCHAR query[] = {
5822 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5823 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5824 MSIQUERY *view;
5825 UINT rc;
5827 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5828 if (rc != ERROR_SUCCESS)
5829 return ERROR_SUCCESS;
5831 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5832 msiobj_release(&view->hdr);
5833 return rc;
5836 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5838 DWORD i, needed, count;
5839 ENUM_SERVICE_STATUSW *dependencies;
5840 SERVICE_STATUS ss;
5841 SC_HANDLE depserv;
5843 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5844 0, &needed, &count))
5845 return TRUE;
5847 if (GetLastError() != ERROR_MORE_DATA)
5848 return FALSE;
5850 dependencies = msi_alloc(needed);
5851 if (!dependencies)
5852 return FALSE;
5854 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5855 needed, &needed, &count))
5856 goto error;
5858 for (i = 0; i < count; i++)
5860 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5861 SERVICE_STOP | SERVICE_QUERY_STATUS);
5862 if (!depserv)
5863 goto error;
5865 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5866 goto error;
5869 return TRUE;
5871 error:
5872 msi_free(dependencies);
5873 return FALSE;
5876 static UINT stop_service( LPCWSTR name )
5878 SC_HANDLE scm = NULL, service = NULL;
5879 SERVICE_STATUS status;
5880 SERVICE_STATUS_PROCESS ssp;
5881 DWORD needed;
5883 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5884 if (!scm)
5886 WARN("Failed to open the SCM: %d\n", GetLastError());
5887 goto done;
5890 service = OpenServiceW(scm, name,
5891 SERVICE_STOP |
5892 SERVICE_QUERY_STATUS |
5893 SERVICE_ENUMERATE_DEPENDENTS);
5894 if (!service)
5896 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5897 goto done;
5900 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5901 sizeof(SERVICE_STATUS_PROCESS), &needed))
5903 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5904 goto done;
5907 if (ssp.dwCurrentState == SERVICE_STOPPED)
5908 goto done;
5910 stop_service_dependents(scm, service);
5912 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5913 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5915 done:
5916 CloseServiceHandle(service);
5917 CloseServiceHandle(scm);
5919 return ERROR_SUCCESS;
5922 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5924 MSIPACKAGE *package = param;
5925 MSICOMPONENT *comp;
5926 MSIRECORD *uirow;
5927 LPCWSTR component;
5928 LPWSTR name = NULL, display_name = NULL;
5929 DWORD event, len;
5930 SC_HANDLE scm;
5932 event = MSI_RecordGetInteger( rec, 3 );
5933 if (!(event & msidbServiceControlEventStop))
5934 return ERROR_SUCCESS;
5936 component = MSI_RecordGetString( rec, 6 );
5937 comp = msi_get_loaded_component( package, component );
5938 if (!comp)
5939 return ERROR_SUCCESS;
5941 comp->Action = msi_get_component_action( package, comp );
5942 if (comp->Action != INSTALLSTATE_ABSENT)
5944 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5945 return ERROR_SUCCESS;
5948 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5949 if (!scm)
5951 ERR("Failed to open the service control manager\n");
5952 goto done;
5955 len = 0;
5956 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5957 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5959 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5960 GetServiceDisplayNameW( scm, name, display_name, &len );
5962 CloseServiceHandle( scm );
5964 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5965 stop_service( name );
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, szStopServices, uirow );
5972 msiobj_release( &uirow->hdr );
5974 msi_free( name );
5975 msi_free( display_name );
5976 return ERROR_SUCCESS;
5979 static UINT ACTION_StopServices( MSIPACKAGE *package )
5981 static const WCHAR query[] = {
5982 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5983 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5984 MSIQUERY *view;
5985 UINT rc;
5987 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5988 if (rc != ERROR_SUCCESS)
5989 return ERROR_SUCCESS;
5991 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5992 msiobj_release(&view->hdr);
5993 return rc;
5996 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
5998 MSIPACKAGE *package = param;
5999 MSICOMPONENT *comp;
6000 MSIRECORD *uirow;
6001 LPWSTR name = NULL, display_name = NULL;
6002 DWORD event, len;
6003 SC_HANDLE scm = NULL, service = NULL;
6005 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6006 if (!comp)
6007 return ERROR_SUCCESS;
6009 event = MSI_RecordGetInteger( rec, 3 );
6010 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6012 comp->Action = msi_get_component_action( package, comp );
6013 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6014 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6016 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6017 msi_free( name );
6018 return ERROR_SUCCESS;
6020 stop_service( name );
6022 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6023 if (!scm)
6025 WARN("Failed to open the SCM: %d\n", GetLastError());
6026 goto done;
6029 len = 0;
6030 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6031 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6033 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6034 GetServiceDisplayNameW( scm, name, display_name, &len );
6037 service = OpenServiceW( scm, name, DELETE );
6038 if (!service)
6040 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6041 goto done;
6044 if (!DeleteService( service ))
6045 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6047 done:
6048 uirow = MSI_CreateRecord( 2 );
6049 MSI_RecordSetStringW( uirow, 1, display_name );
6050 MSI_RecordSetStringW( uirow, 2, name );
6051 msi_ui_actiondata( package, szDeleteServices, uirow );
6052 msiobj_release( &uirow->hdr );
6054 CloseServiceHandle( service );
6055 CloseServiceHandle( scm );
6056 msi_free( name );
6057 msi_free( display_name );
6059 return ERROR_SUCCESS;
6062 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6064 static const WCHAR query[] = {
6065 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6066 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6067 MSIQUERY *view;
6068 UINT rc;
6070 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6071 if (rc != ERROR_SUCCESS)
6072 return ERROR_SUCCESS;
6074 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6075 msiobj_release( &view->hdr );
6076 return rc;
6079 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6081 MSIPACKAGE *package = param;
6082 LPWSTR driver, driver_path, ptr;
6083 WCHAR outpath[MAX_PATH];
6084 MSIFILE *driver_file = NULL, *setup_file = NULL;
6085 MSICOMPONENT *comp;
6086 MSIRECORD *uirow;
6087 LPCWSTR desc, file_key, component;
6088 DWORD len, usage;
6089 UINT r = ERROR_SUCCESS;
6091 static const WCHAR driver_fmt[] = {
6092 'D','r','i','v','e','r','=','%','s',0};
6093 static const WCHAR setup_fmt[] = {
6094 'S','e','t','u','p','=','%','s',0};
6095 static const WCHAR usage_fmt[] = {
6096 'F','i','l','e','U','s','a','g','e','=','1',0};
6098 component = MSI_RecordGetString( rec, 2 );
6099 comp = msi_get_loaded_component( package, component );
6100 if (!comp)
6101 return ERROR_SUCCESS;
6103 comp->Action = msi_get_component_action( package, comp );
6104 if (comp->Action != INSTALLSTATE_LOCAL)
6106 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6107 return ERROR_SUCCESS;
6109 desc = MSI_RecordGetString(rec, 3);
6111 file_key = MSI_RecordGetString( rec, 4 );
6112 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6114 file_key = MSI_RecordGetString( rec, 5 );
6115 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6117 if (!driver_file)
6119 ERR("ODBC Driver entry not found!\n");
6120 return ERROR_FUNCTION_FAILED;
6123 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6124 if (setup_file)
6125 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6126 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6128 driver = msi_alloc(len * sizeof(WCHAR));
6129 if (!driver)
6130 return ERROR_OUTOFMEMORY;
6132 ptr = driver;
6133 lstrcpyW(ptr, desc);
6134 ptr += lstrlenW(ptr) + 1;
6136 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6137 ptr += len + 1;
6139 if (setup_file)
6141 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6142 ptr += len + 1;
6145 lstrcpyW(ptr, usage_fmt);
6146 ptr += lstrlenW(ptr) + 1;
6147 *ptr = '\0';
6149 if (!driver_file->TargetPath)
6151 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6152 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6154 driver_path = strdupW(driver_file->TargetPath);
6155 ptr = strrchrW(driver_path, '\\');
6156 if (ptr) *ptr = '\0';
6158 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6159 NULL, ODBC_INSTALL_COMPLETE, &usage))
6161 ERR("Failed to install SQL driver!\n");
6162 r = ERROR_FUNCTION_FAILED;
6165 uirow = MSI_CreateRecord( 5 );
6166 MSI_RecordSetStringW( uirow, 1, desc );
6167 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6168 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6169 msi_ui_actiondata( package, szInstallODBC, uirow );
6170 msiobj_release( &uirow->hdr );
6172 msi_free(driver);
6173 msi_free(driver_path);
6175 return r;
6178 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6180 MSIPACKAGE *package = param;
6181 LPWSTR translator, translator_path, ptr;
6182 WCHAR outpath[MAX_PATH];
6183 MSIFILE *translator_file = NULL, *setup_file = NULL;
6184 MSICOMPONENT *comp;
6185 MSIRECORD *uirow;
6186 LPCWSTR desc, file_key, component;
6187 DWORD len, usage;
6188 UINT r = ERROR_SUCCESS;
6190 static const WCHAR translator_fmt[] = {
6191 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6192 static const WCHAR setup_fmt[] = {
6193 'S','e','t','u','p','=','%','s',0};
6195 component = MSI_RecordGetString( rec, 2 );
6196 comp = msi_get_loaded_component( package, component );
6197 if (!comp)
6198 return ERROR_SUCCESS;
6200 comp->Action = msi_get_component_action( package, comp );
6201 if (comp->Action != INSTALLSTATE_LOCAL)
6203 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6204 return ERROR_SUCCESS;
6206 desc = MSI_RecordGetString(rec, 3);
6208 file_key = MSI_RecordGetString( rec, 4 );
6209 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6211 file_key = MSI_RecordGetString( rec, 5 );
6212 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6214 if (!translator_file)
6216 ERR("ODBC Translator entry not found!\n");
6217 return ERROR_FUNCTION_FAILED;
6220 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6221 if (setup_file)
6222 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6224 translator = msi_alloc(len * sizeof(WCHAR));
6225 if (!translator)
6226 return ERROR_OUTOFMEMORY;
6228 ptr = translator;
6229 lstrcpyW(ptr, desc);
6230 ptr += lstrlenW(ptr) + 1;
6232 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6233 ptr += len + 1;
6235 if (setup_file)
6237 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6238 ptr += len + 1;
6240 *ptr = '\0';
6242 translator_path = strdupW(translator_file->TargetPath);
6243 ptr = strrchrW(translator_path, '\\');
6244 if (ptr) *ptr = '\0';
6246 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6247 NULL, ODBC_INSTALL_COMPLETE, &usage))
6249 ERR("Failed to install SQL translator!\n");
6250 r = ERROR_FUNCTION_FAILED;
6253 uirow = MSI_CreateRecord( 5 );
6254 MSI_RecordSetStringW( uirow, 1, desc );
6255 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6256 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6257 msi_ui_actiondata( package, szInstallODBC, uirow );
6258 msiobj_release( &uirow->hdr );
6260 msi_free(translator);
6261 msi_free(translator_path);
6263 return r;
6266 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6268 MSIPACKAGE *package = param;
6269 MSICOMPONENT *comp;
6270 LPWSTR attrs;
6271 LPCWSTR desc, driver, component;
6272 WORD request = ODBC_ADD_SYS_DSN;
6273 INT registration;
6274 DWORD len;
6275 UINT r = ERROR_SUCCESS;
6276 MSIRECORD *uirow;
6278 static const WCHAR attrs_fmt[] = {
6279 'D','S','N','=','%','s',0 };
6281 component = MSI_RecordGetString( rec, 2 );
6282 comp = msi_get_loaded_component( package, component );
6283 if (!comp)
6284 return ERROR_SUCCESS;
6286 comp->Action = msi_get_component_action( package, comp );
6287 if (comp->Action != INSTALLSTATE_LOCAL)
6289 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6290 return ERROR_SUCCESS;
6293 desc = MSI_RecordGetString(rec, 3);
6294 driver = MSI_RecordGetString(rec, 4);
6295 registration = MSI_RecordGetInteger(rec, 5);
6297 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6298 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6300 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6301 attrs = msi_alloc(len * sizeof(WCHAR));
6302 if (!attrs)
6303 return ERROR_OUTOFMEMORY;
6305 len = sprintfW(attrs, attrs_fmt, desc);
6306 attrs[len + 1] = 0;
6308 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6310 ERR("Failed to install SQL data source!\n");
6311 r = ERROR_FUNCTION_FAILED;
6314 uirow = MSI_CreateRecord( 5 );
6315 MSI_RecordSetStringW( uirow, 1, desc );
6316 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6317 MSI_RecordSetInteger( uirow, 3, request );
6318 msi_ui_actiondata( package, szInstallODBC, uirow );
6319 msiobj_release( &uirow->hdr );
6321 msi_free(attrs);
6323 return r;
6326 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6328 static const WCHAR driver_query[] = {
6329 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6330 'O','D','B','C','D','r','i','v','e','r',0};
6331 static const WCHAR translator_query[] = {
6332 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6333 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6334 static const WCHAR source_query[] = {
6335 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6336 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6337 MSIQUERY *view;
6338 UINT rc;
6340 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6341 if (rc == ERROR_SUCCESS)
6343 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6344 msiobj_release(&view->hdr);
6345 if (rc != ERROR_SUCCESS)
6346 return rc;
6348 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6349 if (rc == ERROR_SUCCESS)
6351 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6352 msiobj_release(&view->hdr);
6353 if (rc != ERROR_SUCCESS)
6354 return rc;
6356 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6357 if (rc == ERROR_SUCCESS)
6359 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6360 msiobj_release(&view->hdr);
6361 if (rc != ERROR_SUCCESS)
6362 return rc;
6364 return ERROR_SUCCESS;
6367 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6369 MSIPACKAGE *package = param;
6370 MSICOMPONENT *comp;
6371 MSIRECORD *uirow;
6372 DWORD usage;
6373 LPCWSTR desc, component;
6375 component = MSI_RecordGetString( rec, 2 );
6376 comp = msi_get_loaded_component( package, component );
6377 if (!comp)
6378 return ERROR_SUCCESS;
6380 comp->Action = msi_get_component_action( package, comp );
6381 if (comp->Action != INSTALLSTATE_ABSENT)
6383 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6384 return ERROR_SUCCESS;
6387 desc = MSI_RecordGetString( rec, 3 );
6388 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6390 WARN("Failed to remove ODBC driver\n");
6392 else if (!usage)
6394 FIXME("Usage count reached 0\n");
6397 uirow = MSI_CreateRecord( 2 );
6398 MSI_RecordSetStringW( uirow, 1, desc );
6399 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6400 msi_ui_actiondata( package, szRemoveODBC, uirow );
6401 msiobj_release( &uirow->hdr );
6403 return ERROR_SUCCESS;
6406 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6408 MSIPACKAGE *package = param;
6409 MSICOMPONENT *comp;
6410 MSIRECORD *uirow;
6411 DWORD usage;
6412 LPCWSTR desc, component;
6414 component = MSI_RecordGetString( rec, 2 );
6415 comp = msi_get_loaded_component( package, component );
6416 if (!comp)
6417 return ERROR_SUCCESS;
6419 comp->Action = msi_get_component_action( package, comp );
6420 if (comp->Action != INSTALLSTATE_ABSENT)
6422 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6423 return ERROR_SUCCESS;
6426 desc = MSI_RecordGetString( rec, 3 );
6427 if (!SQLRemoveTranslatorW( desc, &usage ))
6429 WARN("Failed to remove ODBC translator\n");
6431 else if (!usage)
6433 FIXME("Usage count reached 0\n");
6436 uirow = MSI_CreateRecord( 2 );
6437 MSI_RecordSetStringW( uirow, 1, desc );
6438 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6439 msi_ui_actiondata( package, szRemoveODBC, uirow );
6440 msiobj_release( &uirow->hdr );
6442 return ERROR_SUCCESS;
6445 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6447 MSIPACKAGE *package = param;
6448 MSICOMPONENT *comp;
6449 MSIRECORD *uirow;
6450 LPWSTR attrs;
6451 LPCWSTR desc, driver, component;
6452 WORD request = ODBC_REMOVE_SYS_DSN;
6453 INT registration;
6454 DWORD len;
6456 static const WCHAR attrs_fmt[] = {
6457 'D','S','N','=','%','s',0 };
6459 component = MSI_RecordGetString( rec, 2 );
6460 comp = msi_get_loaded_component( package, component );
6461 if (!comp)
6462 return ERROR_SUCCESS;
6464 comp->Action = msi_get_component_action( package, comp );
6465 if (comp->Action != INSTALLSTATE_ABSENT)
6467 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6468 return ERROR_SUCCESS;
6471 desc = MSI_RecordGetString( rec, 3 );
6472 driver = MSI_RecordGetString( rec, 4 );
6473 registration = MSI_RecordGetInteger( rec, 5 );
6475 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6476 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6478 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6479 attrs = msi_alloc( len * sizeof(WCHAR) );
6480 if (!attrs)
6481 return ERROR_OUTOFMEMORY;
6483 FIXME("Use ODBCSourceAttribute table\n");
6485 len = sprintfW( attrs, attrs_fmt, desc );
6486 attrs[len + 1] = 0;
6488 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6490 WARN("Failed to remove ODBC data source\n");
6492 msi_free( attrs );
6494 uirow = MSI_CreateRecord( 3 );
6495 MSI_RecordSetStringW( uirow, 1, desc );
6496 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6497 MSI_RecordSetInteger( uirow, 3, request );
6498 msi_ui_actiondata( package, szRemoveODBC, uirow );
6499 msiobj_release( &uirow->hdr );
6501 return ERROR_SUCCESS;
6504 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6506 static const WCHAR driver_query[] = {
6507 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6508 'O','D','B','C','D','r','i','v','e','r',0};
6509 static const WCHAR translator_query[] = {
6510 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6511 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6512 static const WCHAR source_query[] = {
6513 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6514 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6515 MSIQUERY *view;
6516 UINT rc;
6518 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6519 if (rc == ERROR_SUCCESS)
6521 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6522 msiobj_release( &view->hdr );
6523 if (rc != ERROR_SUCCESS)
6524 return rc;
6526 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6527 if (rc == ERROR_SUCCESS)
6529 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6530 msiobj_release( &view->hdr );
6531 if (rc != ERROR_SUCCESS)
6532 return rc;
6534 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6535 if (rc == ERROR_SUCCESS)
6537 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6538 msiobj_release( &view->hdr );
6539 if (rc != ERROR_SUCCESS)
6540 return rc;
6542 return ERROR_SUCCESS;
6545 #define ENV_ACT_SETALWAYS 0x1
6546 #define ENV_ACT_SETABSENT 0x2
6547 #define ENV_ACT_REMOVE 0x4
6548 #define ENV_ACT_REMOVEMATCH 0x8
6550 #define ENV_MOD_MACHINE 0x20000000
6551 #define ENV_MOD_APPEND 0x40000000
6552 #define ENV_MOD_PREFIX 0x80000000
6553 #define ENV_MOD_MASK 0xC0000000
6555 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6557 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6559 LPCWSTR cptr = *name;
6561 static const WCHAR prefix[] = {'[','~',']',0};
6562 static const int prefix_len = 3;
6564 *flags = 0;
6565 while (*cptr)
6567 if (*cptr == '=')
6568 *flags |= ENV_ACT_SETALWAYS;
6569 else if (*cptr == '+')
6570 *flags |= ENV_ACT_SETABSENT;
6571 else if (*cptr == '-')
6572 *flags |= ENV_ACT_REMOVE;
6573 else if (*cptr == '!')
6574 *flags |= ENV_ACT_REMOVEMATCH;
6575 else if (*cptr == '*')
6576 *flags |= ENV_MOD_MACHINE;
6577 else
6578 break;
6580 cptr++;
6581 (*name)++;
6584 if (!*cptr)
6586 ERR("Missing environment variable\n");
6587 return ERROR_FUNCTION_FAILED;
6590 if (*value)
6592 LPCWSTR ptr = *value;
6593 if (!strncmpW(ptr, prefix, prefix_len))
6595 if (ptr[prefix_len] == szSemiColon[0])
6597 *flags |= ENV_MOD_APPEND;
6598 *value += lstrlenW(prefix);
6600 else
6602 *value = NULL;
6605 else if (lstrlenW(*value) >= prefix_len)
6607 ptr += lstrlenW(ptr) - prefix_len;
6608 if (!strcmpW( ptr, prefix ))
6610 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6612 *flags |= ENV_MOD_PREFIX;
6613 /* the "[~]" will be removed by deformat_string */;
6615 else
6617 *value = NULL;
6623 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6624 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6625 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6626 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6628 ERR("Invalid flags: %08x\n", *flags);
6629 return ERROR_FUNCTION_FAILED;
6632 if (!*flags)
6633 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6635 return ERROR_SUCCESS;
6638 static UINT open_env_key( DWORD flags, HKEY *key )
6640 static const WCHAR user_env[] =
6641 {'E','n','v','i','r','o','n','m','e','n','t',0};
6642 static const WCHAR machine_env[] =
6643 {'S','y','s','t','e','m','\\',
6644 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6645 'C','o','n','t','r','o','l','\\',
6646 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6647 'E','n','v','i','r','o','n','m','e','n','t',0};
6648 const WCHAR *env;
6649 HKEY root;
6650 LONG res;
6652 if (flags & ENV_MOD_MACHINE)
6654 env = machine_env;
6655 root = HKEY_LOCAL_MACHINE;
6657 else
6659 env = user_env;
6660 root = HKEY_CURRENT_USER;
6663 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6664 if (res != ERROR_SUCCESS)
6666 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6667 return ERROR_FUNCTION_FAILED;
6670 return ERROR_SUCCESS;
6673 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6675 MSIPACKAGE *package = param;
6676 LPCWSTR name, value, component;
6677 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6678 DWORD flags, type, size;
6679 UINT res;
6680 HKEY env = NULL;
6681 MSICOMPONENT *comp;
6682 MSIRECORD *uirow;
6683 int action = 0;
6685 component = MSI_RecordGetString(rec, 4);
6686 comp = msi_get_loaded_component(package, component);
6687 if (!comp)
6688 return ERROR_SUCCESS;
6690 comp->Action = msi_get_component_action( package, comp );
6691 if (comp->Action != INSTALLSTATE_LOCAL)
6693 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6694 return ERROR_SUCCESS;
6696 name = MSI_RecordGetString(rec, 2);
6697 value = MSI_RecordGetString(rec, 3);
6699 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6701 res = env_parse_flags(&name, &value, &flags);
6702 if (res != ERROR_SUCCESS || !value)
6703 goto done;
6705 if (value && !deformat_string(package, value, &deformatted))
6707 res = ERROR_OUTOFMEMORY;
6708 goto done;
6711 value = deformatted;
6713 res = open_env_key( flags, &env );
6714 if (res != ERROR_SUCCESS)
6715 goto done;
6717 if (flags & ENV_MOD_MACHINE)
6718 action |= 0x20000000;
6720 size = 0;
6721 type = REG_SZ;
6722 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6723 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6724 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6725 goto done;
6727 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6729 action = 0x2;
6731 /* Nothing to do. */
6732 if (!value)
6734 res = ERROR_SUCCESS;
6735 goto done;
6738 /* If we are appending but the string was empty, strip ; */
6739 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6741 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6742 newval = strdupW(value);
6743 if (!newval)
6745 res = ERROR_OUTOFMEMORY;
6746 goto done;
6749 else
6751 action = 0x1;
6753 /* Contrary to MSDN, +-variable to [~];path works */
6754 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6756 res = ERROR_SUCCESS;
6757 goto done;
6760 data = msi_alloc(size);
6761 if (!data)
6763 RegCloseKey(env);
6764 return ERROR_OUTOFMEMORY;
6767 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6768 if (res != ERROR_SUCCESS)
6769 goto done;
6771 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6773 action = 0x4;
6774 res = RegDeleteValueW(env, name);
6775 if (res != ERROR_SUCCESS)
6776 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6777 goto done;
6780 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6781 if (flags & ENV_MOD_MASK)
6783 DWORD mod_size;
6784 int multiplier = 0;
6785 if (flags & ENV_MOD_APPEND) multiplier++;
6786 if (flags & ENV_MOD_PREFIX) multiplier++;
6787 mod_size = lstrlenW(value) * multiplier;
6788 size += mod_size * sizeof(WCHAR);
6791 newval = msi_alloc(size);
6792 ptr = newval;
6793 if (!newval)
6795 res = ERROR_OUTOFMEMORY;
6796 goto done;
6799 if (flags & ENV_MOD_PREFIX)
6801 lstrcpyW(newval, value);
6802 ptr = newval + lstrlenW(value);
6803 action |= 0x80000000;
6806 lstrcpyW(ptr, data);
6808 if (flags & ENV_MOD_APPEND)
6810 lstrcatW(newval, value);
6811 action |= 0x40000000;
6814 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6815 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6816 if (res)
6818 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6821 done:
6822 uirow = MSI_CreateRecord( 3 );
6823 MSI_RecordSetStringW( uirow, 1, name );
6824 MSI_RecordSetStringW( uirow, 2, newval );
6825 MSI_RecordSetInteger( uirow, 3, action );
6826 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6827 msiobj_release( &uirow->hdr );
6829 if (env) RegCloseKey(env);
6830 msi_free(deformatted);
6831 msi_free(data);
6832 msi_free(newval);
6833 return res;
6836 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6838 static const WCHAR query[] = {
6839 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6840 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6841 MSIQUERY *view;
6842 UINT rc;
6844 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6845 if (rc != ERROR_SUCCESS)
6846 return ERROR_SUCCESS;
6848 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6849 msiobj_release(&view->hdr);
6850 return rc;
6853 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6855 MSIPACKAGE *package = param;
6856 LPCWSTR name, value, component;
6857 LPWSTR deformatted = NULL;
6858 DWORD flags;
6859 HKEY env;
6860 MSICOMPONENT *comp;
6861 MSIRECORD *uirow;
6862 int action = 0;
6863 LONG res;
6864 UINT r;
6866 component = MSI_RecordGetString( rec, 4 );
6867 comp = msi_get_loaded_component( package, component );
6868 if (!comp)
6869 return ERROR_SUCCESS;
6871 comp->Action = msi_get_component_action( package, comp );
6872 if (comp->Action != INSTALLSTATE_ABSENT)
6874 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6875 return ERROR_SUCCESS;
6877 name = MSI_RecordGetString( rec, 2 );
6878 value = MSI_RecordGetString( rec, 3 );
6880 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6882 r = env_parse_flags( &name, &value, &flags );
6883 if (r != ERROR_SUCCESS)
6884 return r;
6886 if (!(flags & ENV_ACT_REMOVE))
6888 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6889 return ERROR_SUCCESS;
6892 if (value && !deformat_string( package, value, &deformatted ))
6893 return ERROR_OUTOFMEMORY;
6895 value = deformatted;
6897 r = open_env_key( flags, &env );
6898 if (r != ERROR_SUCCESS)
6900 r = ERROR_SUCCESS;
6901 goto done;
6904 if (flags & ENV_MOD_MACHINE)
6905 action |= 0x20000000;
6907 TRACE("Removing %s\n", debugstr_w(name));
6909 res = RegDeleteValueW( env, name );
6910 if (res != ERROR_SUCCESS)
6912 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6913 r = ERROR_SUCCESS;
6916 done:
6917 uirow = MSI_CreateRecord( 3 );
6918 MSI_RecordSetStringW( uirow, 1, name );
6919 MSI_RecordSetStringW( uirow, 2, value );
6920 MSI_RecordSetInteger( uirow, 3, action );
6921 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6922 msiobj_release( &uirow->hdr );
6924 if (env) RegCloseKey( env );
6925 msi_free( deformatted );
6926 return r;
6929 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6931 static const WCHAR query[] = {
6932 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6933 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6934 MSIQUERY *view;
6935 UINT rc;
6937 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6938 if (rc != ERROR_SUCCESS)
6939 return ERROR_SUCCESS;
6941 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6942 msiobj_release( &view->hdr );
6943 return rc;
6946 UINT msi_validate_product_id( MSIPACKAGE *package )
6948 LPWSTR key, template, id;
6949 UINT r = ERROR_SUCCESS;
6951 id = msi_dup_property( package->db, szProductID );
6952 if (id)
6954 msi_free( id );
6955 return ERROR_SUCCESS;
6957 template = msi_dup_property( package->db, szPIDTemplate );
6958 key = msi_dup_property( package->db, szPIDKEY );
6959 if (key && template)
6961 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6962 r = msi_set_property( package->db, szProductID, key );
6964 msi_free( template );
6965 msi_free( key );
6966 return r;
6969 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6971 return msi_validate_product_id( package );
6974 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6976 TRACE("\n");
6977 package->need_reboot_at_end = 1;
6978 return ERROR_SUCCESS;
6981 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6983 static const WCHAR szAvailableFreeReg[] =
6984 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6985 MSIRECORD *uirow;
6986 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
6988 TRACE("%p %d kilobytes\n", package, space);
6990 uirow = MSI_CreateRecord( 1 );
6991 MSI_RecordSetInteger( uirow, 1, space );
6992 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
6993 msiobj_release( &uirow->hdr );
6995 return ERROR_SUCCESS;
6998 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7000 TRACE("%p\n", package);
7002 msi_set_property( package->db, szRollbackDisabled, szOne );
7003 return ERROR_SUCCESS;
7006 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7008 FIXME("%p\n", package);
7009 return ERROR_SUCCESS;
7012 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7014 static const WCHAR driver_query[] = {
7015 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7016 'O','D','B','C','D','r','i','v','e','r',0};
7017 static const WCHAR translator_query[] = {
7018 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7019 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7020 MSIQUERY *view;
7021 UINT r, count;
7023 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7024 if (r == ERROR_SUCCESS)
7026 count = 0;
7027 r = MSI_IterateRecords( view, &count, NULL, package );
7028 msiobj_release( &view->hdr );
7029 if (r != ERROR_SUCCESS)
7030 return r;
7031 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7033 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7034 if (r == ERROR_SUCCESS)
7036 count = 0;
7037 r = MSI_IterateRecords( view, &count, NULL, package );
7038 msiobj_release( &view->hdr );
7039 if (r != ERROR_SUCCESS)
7040 return r;
7041 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7043 return ERROR_SUCCESS;
7046 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7048 static const WCHAR fmtW[] =
7049 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7050 MSIPACKAGE *package = param;
7051 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7052 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7053 WCHAR *product, *features, *cmd;
7054 STARTUPINFOW si;
7055 PROCESS_INFORMATION info;
7056 BOOL ret;
7058 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7060 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7062 len += strlenW( product );
7063 if (features)
7064 len += strlenW( features );
7065 else
7066 len += sizeof(szAll) / sizeof(szAll[0]);
7068 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7070 msi_free( product );
7071 msi_free( features );
7072 return ERROR_OUTOFMEMORY;
7074 sprintfW( cmd, fmtW, product, features ? features : szAll );
7075 msi_free( product );
7076 msi_free( features );
7078 memset( &si, 0, sizeof(STARTUPINFOW) );
7079 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7080 msi_free( cmd );
7081 if (!ret) return GetLastError();
7082 CloseHandle( info.hThread );
7084 WaitForSingleObject( info.hProcess, INFINITE );
7085 CloseHandle( info.hProcess );
7086 return ERROR_SUCCESS;
7089 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7091 static const WCHAR query[] = {
7092 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7093 MSIQUERY *view;
7094 UINT r;
7096 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7097 if (r == ERROR_SUCCESS)
7099 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7100 msiobj_release( &view->hdr );
7101 if (r != ERROR_SUCCESS)
7102 return r;
7104 return ERROR_SUCCESS;
7107 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7109 MSIPACKAGE *package = param;
7110 int attributes = MSI_RecordGetInteger( rec, 5 );
7112 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7114 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7115 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7116 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7117 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7118 HKEY hkey;
7119 UINT r;
7121 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7123 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7124 if (r != ERROR_SUCCESS)
7125 return ERROR_SUCCESS;
7127 else
7129 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7130 if (r != ERROR_SUCCESS)
7131 return ERROR_SUCCESS;
7133 RegCloseKey( hkey );
7135 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7136 debugstr_w(upgrade_code), debugstr_w(version_min),
7137 debugstr_w(version_max), debugstr_w(language));
7139 return ERROR_SUCCESS;
7142 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7144 static const WCHAR query[] = {
7145 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7146 'U','p','g','r','a','d','e',0};
7147 MSIQUERY *view;
7148 UINT r;
7150 if (msi_get_property_int( package->db, szInstalled, 0 ))
7152 TRACE("product is installed, skipping action\n");
7153 return ERROR_SUCCESS;
7155 if (msi_get_property_int( package->db, szPreselected, 0 ))
7157 TRACE("Preselected property is set, not migrating feature states\n");
7158 return ERROR_SUCCESS;
7160 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7161 if (r == ERROR_SUCCESS)
7163 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7164 msiobj_release( &view->hdr );
7165 if (r != ERROR_SUCCESS)
7166 return r;
7168 return ERROR_SUCCESS;
7171 static void bind_image( const char *filename, const char *path )
7173 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7175 WARN("failed to bind image %u\n", GetLastError());
7179 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7181 UINT i;
7182 MSIFILE *file;
7183 MSIPACKAGE *package = param;
7184 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7185 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7186 char *filenameA, *pathA;
7187 WCHAR *pathW, **path_list;
7189 if (!(file = msi_get_loaded_file( package, key )))
7191 WARN("file %s not found\n", debugstr_w(key));
7192 return ERROR_SUCCESS;
7194 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7195 path_list = msi_split_string( paths, ';' );
7196 if (!path_list) bind_image( filenameA, NULL );
7197 else
7199 for (i = 0; path_list[i] && path_list[i][0]; i++)
7201 deformat_string( package, path_list[i], &pathW );
7202 if ((pathA = strdupWtoA( pathW )))
7204 bind_image( filenameA, pathA );
7205 msi_free( pathA );
7207 msi_free( pathW );
7210 msi_free( path_list );
7211 msi_free( filenameA );
7212 return ERROR_SUCCESS;
7215 static UINT ACTION_BindImage( MSIPACKAGE *package )
7217 static const WCHAR query[] = {
7218 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7219 'B','i','n','d','I','m','a','g','e',0};
7220 MSIQUERY *view;
7221 UINT r;
7223 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7224 if (r == ERROR_SUCCESS)
7226 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7227 msiobj_release( &view->hdr );
7228 if (r != ERROR_SUCCESS)
7229 return r;
7231 return ERROR_SUCCESS;
7234 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7236 static const WCHAR query[] = {
7237 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7238 MSIQUERY *view;
7239 DWORD count = 0;
7240 UINT r;
7242 r = MSI_OpenQuery( package->db, &view, query, table );
7243 if (r == ERROR_SUCCESS)
7245 r = MSI_IterateRecords(view, &count, NULL, package);
7246 msiobj_release(&view->hdr);
7247 if (r != ERROR_SUCCESS)
7248 return r;
7250 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7251 return ERROR_SUCCESS;
7254 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7256 static const WCHAR table[] = {
7257 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7258 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7261 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7263 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7264 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7267 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7269 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7270 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7273 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7275 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7276 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7279 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7281 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7282 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7285 static const struct
7287 const WCHAR *action;
7288 UINT (*handler)(MSIPACKAGE *);
7289 const WCHAR *action_rollback;
7291 StandardActions[] =
7293 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7294 { szAppSearch, ACTION_AppSearch, NULL },
7295 { szBindImage, ACTION_BindImage, NULL },
7296 { szCCPSearch, ACTION_CCPSearch, NULL },
7297 { szCostFinalize, ACTION_CostFinalize, NULL },
7298 { szCostInitialize, ACTION_CostInitialize, NULL },
7299 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7300 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7301 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7302 { szDisableRollback, ACTION_DisableRollback, NULL },
7303 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7304 { szExecuteAction, ACTION_ExecuteAction, NULL },
7305 { szFileCost, ACTION_FileCost, NULL },
7306 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7307 { szForceReboot, ACTION_ForceReboot, NULL },
7308 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7309 { szInstallExecute, ACTION_InstallExecute, NULL },
7310 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7311 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7312 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7313 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7314 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7315 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7316 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7317 { szInstallValidate, ACTION_InstallValidate, NULL },
7318 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7319 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7320 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7321 { szMoveFiles, ACTION_MoveFiles, NULL },
7322 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7323 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7324 { szPatchFiles, ACTION_PatchFiles, NULL },
7325 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7326 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7327 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7328 { szPublishProduct, ACTION_PublishProduct, NULL },
7329 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7330 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7331 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7332 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7333 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7334 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7335 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7336 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7337 { szRegisterUser, ACTION_RegisterUser, NULL },
7338 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7339 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7340 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7341 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7342 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7343 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7344 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7345 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7346 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7347 { szResolveSource, ACTION_ResolveSource, NULL },
7348 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7349 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7350 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7351 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7352 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7353 { szStartServices, ACTION_StartServices, szStopServices },
7354 { szStopServices, ACTION_StopServices, szStartServices },
7355 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7356 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7357 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7358 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7359 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7360 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7361 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7362 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7363 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7364 { szValidateProductID, ACTION_ValidateProductID, NULL },
7365 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7366 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7367 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7368 { NULL, NULL, NULL }
7371 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7373 BOOL ret = FALSE;
7374 UINT i;
7376 i = 0;
7377 while (StandardActions[i].action != NULL)
7379 if (!strcmpW( StandardActions[i].action, action ))
7381 ui_actionstart( package, action );
7382 if (StandardActions[i].handler)
7384 ui_actioninfo( package, action, TRUE, 0 );
7385 *rc = StandardActions[i].handler( package );
7386 ui_actioninfo( package, action, FALSE, *rc );
7388 if (StandardActions[i].action_rollback && !package->need_rollback)
7390 TRACE("scheduling rollback action\n");
7391 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7394 else
7396 FIXME("unhandled standard action %s\n", debugstr_w(action));
7397 *rc = ERROR_SUCCESS;
7399 ret = TRUE;
7400 break;
7402 i++;
7404 return ret;
7407 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7409 UINT rc = ERROR_SUCCESS;
7410 BOOL handled;
7412 TRACE("Performing action (%s)\n", debugstr_w(action));
7414 handled = ACTION_HandleStandardAction(package, action, &rc);
7416 if (!handled)
7417 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7419 if (!handled)
7421 WARN("unhandled msi action %s\n", debugstr_w(action));
7422 rc = ERROR_FUNCTION_NOT_CALLED;
7425 return rc;
7428 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7430 UINT rc = ERROR_SUCCESS;
7431 BOOL handled = FALSE;
7433 TRACE("Performing action (%s)\n", debugstr_w(action));
7435 package->action_progress_increment = 0;
7436 handled = ACTION_HandleStandardAction(package, action, &rc);
7438 if (!handled)
7439 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7441 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7442 handled = TRUE;
7444 if (!handled)
7446 WARN("unhandled msi action %s\n", debugstr_w(action));
7447 rc = ERROR_FUNCTION_NOT_CALLED;
7450 return rc;
7453 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7455 UINT rc = ERROR_SUCCESS;
7456 MSIRECORD *row;
7458 static const WCHAR query[] =
7459 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7460 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7461 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7462 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7463 static const WCHAR ui_query[] =
7464 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7465 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7466 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7467 ' ', '=',' ','%','i',0};
7469 if (needs_ui_sequence(package))
7470 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7471 else
7472 row = MSI_QueryGetRecord(package->db, query, seq);
7474 if (row)
7476 LPCWSTR action, cond;
7478 TRACE("Running the actions\n");
7480 /* check conditions */
7481 cond = MSI_RecordGetString(row, 2);
7483 /* this is a hack to skip errors in the condition code */
7484 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7486 msiobj_release(&row->hdr);
7487 return ERROR_SUCCESS;
7490 action = MSI_RecordGetString(row, 1);
7491 if (!action)
7493 ERR("failed to fetch action\n");
7494 msiobj_release(&row->hdr);
7495 return ERROR_FUNCTION_FAILED;
7498 if (needs_ui_sequence(package))
7499 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7500 else
7501 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7503 msiobj_release(&row->hdr);
7506 return rc;
7509 /****************************************************
7510 * TOP level entry points
7511 *****************************************************/
7513 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7514 LPCWSTR szCommandLine )
7516 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7517 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7518 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7519 WCHAR *reinstall = NULL;
7520 BOOL ui_exists;
7521 UINT rc;
7523 msi_set_property( package->db, szAction, szInstall );
7525 package->script->InWhatSequence = SEQUENCE_INSTALL;
7527 if (szPackagePath)
7529 LPWSTR p, dir;
7530 LPCWSTR file;
7532 dir = strdupW(szPackagePath);
7533 p = strrchrW(dir, '\\');
7534 if (p)
7536 *(++p) = 0;
7537 file = szPackagePath + (p - dir);
7539 else
7541 msi_free(dir);
7542 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7543 GetCurrentDirectoryW(MAX_PATH, dir);
7544 lstrcatW(dir, szBackSlash);
7545 file = szPackagePath;
7548 msi_free( package->PackagePath );
7549 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7550 if (!package->PackagePath)
7552 msi_free(dir);
7553 return ERROR_OUTOFMEMORY;
7556 lstrcpyW(package->PackagePath, dir);
7557 lstrcatW(package->PackagePath, file);
7558 msi_free(dir);
7560 msi_set_sourcedir_props(package, FALSE);
7563 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7564 if (rc != ERROR_SUCCESS)
7565 return rc;
7567 msi_apply_transforms( package );
7568 msi_apply_patches( package );
7570 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7572 TRACE("setting reinstall property\n");
7573 msi_set_property( package->db, szReinstall, szAll );
7576 /* properties may have been added by a transform */
7577 msi_clone_properties( package );
7579 msi_parse_command_line( package, szCommandLine, FALSE );
7580 msi_adjust_privilege_properties( package );
7581 msi_set_context( package );
7583 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7585 TRACE("disabling rollback\n");
7586 msi_set_property( package->db, szRollbackDisabled, szOne );
7589 if (needs_ui_sequence( package))
7591 package->script->InWhatSequence |= SEQUENCE_UI;
7592 rc = ACTION_ProcessUISequence(package);
7593 ui_exists = ui_sequence_exists(package);
7594 if (rc == ERROR_SUCCESS || !ui_exists)
7596 package->script->InWhatSequence |= SEQUENCE_EXEC;
7597 rc = ACTION_ProcessExecSequence(package, ui_exists);
7600 else
7601 rc = ACTION_ProcessExecSequence(package, FALSE);
7603 package->script->CurrentlyScripting = FALSE;
7605 /* process the ending type action */
7606 if (rc == ERROR_SUCCESS)
7607 ACTION_PerformActionSequence(package, -1);
7608 else if (rc == ERROR_INSTALL_USEREXIT)
7609 ACTION_PerformActionSequence(package, -2);
7610 else if (rc == ERROR_INSTALL_SUSPEND)
7611 ACTION_PerformActionSequence(package, -4);
7612 else /* failed */
7614 ACTION_PerformActionSequence(package, -3);
7615 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7617 package->need_rollback = TRUE;
7621 /* finish up running custom actions */
7622 ACTION_FinishCustomActions(package);
7624 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7626 WARN("installation failed, running rollback script\n");
7627 execute_script( package, SCRIPT_ROLLBACK );
7629 msi_free( reinstall );
7631 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7632 return ERROR_SUCCESS_REBOOT_REQUIRED;
7634 return rc;