jscript: Use jsstr_t for passing strings to regexp matching functions.
[wine.git] / dlls / msi / action.c
blobf29be2b0390f95174b06071345c0cb75c7ba3712
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 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
814 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
815 return INSTALLSTATE_UNKNOWN;
817 return comp->ActionRequest;
820 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
822 if (package->need_rollback) return feature->Installed;
823 return feature->ActionRequest;
826 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
828 MSIPACKAGE *package = param;
829 LPCWSTR dir, component, full_path;
830 MSIRECORD *uirow;
831 MSIFOLDER *folder;
832 MSICOMPONENT *comp;
834 component = MSI_RecordGetString(row, 2);
835 if (!component)
836 return ERROR_SUCCESS;
838 comp = msi_get_loaded_component(package, component);
839 if (!comp)
840 return ERROR_SUCCESS;
842 comp->Action = msi_get_component_action( package, comp );
843 if (comp->Action != INSTALLSTATE_LOCAL)
845 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
846 return ERROR_SUCCESS;
849 dir = MSI_RecordGetString(row,1);
850 if (!dir)
852 ERR("Unable to get folder id\n");
853 return ERROR_SUCCESS;
856 uirow = MSI_CreateRecord(1);
857 MSI_RecordSetStringW(uirow, 1, dir);
858 msi_ui_actiondata(package, szCreateFolders, uirow);
859 msiobj_release(&uirow->hdr);
861 full_path = msi_get_target_folder( package, dir );
862 if (!full_path)
864 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
865 return ERROR_SUCCESS;
867 TRACE("folder is %s\n", debugstr_w(full_path));
869 folder = msi_get_loaded_folder( package, dir );
870 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
871 folder->State = FOLDER_STATE_CREATED;
872 return ERROR_SUCCESS;
875 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
877 static const WCHAR query[] = {
878 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
879 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
880 MSIQUERY *view;
881 UINT rc;
883 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
884 if (rc != ERROR_SUCCESS)
885 return ERROR_SUCCESS;
887 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
888 msiobj_release(&view->hdr);
889 return rc;
892 static void remove_persistent_folder( MSIFOLDER *folder )
894 FolderList *fl;
896 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
898 remove_persistent_folder( fl->folder );
900 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
902 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
906 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
908 MSIPACKAGE *package = param;
909 LPCWSTR dir, component, full_path;
910 MSIRECORD *uirow;
911 MSIFOLDER *folder;
912 MSICOMPONENT *comp;
914 component = MSI_RecordGetString(row, 2);
915 if (!component)
916 return ERROR_SUCCESS;
918 comp = msi_get_loaded_component(package, component);
919 if (!comp)
920 return ERROR_SUCCESS;
922 comp->Action = msi_get_component_action( package, comp );
923 if (comp->Action != INSTALLSTATE_ABSENT)
925 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
926 return ERROR_SUCCESS;
929 dir = MSI_RecordGetString( row, 1 );
930 if (!dir)
932 ERR("Unable to get folder id\n");
933 return ERROR_SUCCESS;
936 full_path = msi_get_target_folder( package, dir );
937 if (!full_path)
939 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
940 return ERROR_SUCCESS;
942 TRACE("folder is %s\n", debugstr_w(full_path));
944 uirow = MSI_CreateRecord( 1 );
945 MSI_RecordSetStringW( uirow, 1, dir );
946 msi_ui_actiondata( package, szRemoveFolders, uirow );
947 msiobj_release( &uirow->hdr );
949 folder = msi_get_loaded_folder( package, dir );
950 remove_persistent_folder( folder );
951 return ERROR_SUCCESS;
954 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
956 static const WCHAR query[] = {
957 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
958 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
959 MSIQUERY *view;
960 UINT rc;
962 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
963 if (rc != ERROR_SUCCESS)
964 return ERROR_SUCCESS;
966 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
967 msiobj_release( &view->hdr );
968 return rc;
971 static UINT load_component( MSIRECORD *row, LPVOID param )
973 MSIPACKAGE *package = param;
974 MSICOMPONENT *comp;
976 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
977 if (!comp)
978 return ERROR_FUNCTION_FAILED;
980 list_add_tail( &package->components, &comp->entry );
982 /* fill in the data */
983 comp->Component = msi_dup_record_field( row, 1 );
985 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
987 comp->ComponentId = msi_dup_record_field( row, 2 );
988 comp->Directory = msi_dup_record_field( row, 3 );
989 comp->Attributes = MSI_RecordGetInteger(row,4);
990 comp->Condition = msi_dup_record_field( row, 5 );
991 comp->KeyPath = msi_dup_record_field( row, 6 );
993 comp->Installed = INSTALLSTATE_UNKNOWN;
994 comp->Action = INSTALLSTATE_UNKNOWN;
995 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
997 comp->assembly = msi_load_assembly( package, comp );
998 return ERROR_SUCCESS;
1001 UINT msi_load_all_components( MSIPACKAGE *package )
1003 static const WCHAR query[] = {
1004 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1005 '`','C','o','m','p','o','n','e','n','t','`',0};
1006 MSIQUERY *view;
1007 UINT r;
1009 if (!list_empty(&package->components))
1010 return ERROR_SUCCESS;
1012 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1013 if (r != ERROR_SUCCESS)
1014 return r;
1016 if (!msi_init_assembly_caches( package ))
1018 ERR("can't initialize assembly caches\n");
1019 msiobj_release( &view->hdr );
1020 return ERROR_FUNCTION_FAILED;
1023 r = MSI_IterateRecords(view, NULL, load_component, package);
1024 msiobj_release(&view->hdr);
1025 return r;
1028 typedef struct {
1029 MSIPACKAGE *package;
1030 MSIFEATURE *feature;
1031 } _ilfs;
1033 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1035 ComponentList *cl;
1037 cl = msi_alloc( sizeof (*cl) );
1038 if ( !cl )
1039 return ERROR_NOT_ENOUGH_MEMORY;
1040 cl->component = comp;
1041 list_add_tail( &feature->Components, &cl->entry );
1043 return ERROR_SUCCESS;
1046 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1048 FeatureList *fl;
1050 fl = msi_alloc( sizeof(*fl) );
1051 if ( !fl )
1052 return ERROR_NOT_ENOUGH_MEMORY;
1053 fl->feature = child;
1054 list_add_tail( &parent->Children, &fl->entry );
1056 return ERROR_SUCCESS;
1059 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1061 _ilfs* ilfs = param;
1062 LPCWSTR component;
1063 MSICOMPONENT *comp;
1065 component = MSI_RecordGetString(row,1);
1067 /* check to see if the component is already loaded */
1068 comp = msi_get_loaded_component( ilfs->package, component );
1069 if (!comp)
1071 WARN("ignoring unknown component %s\n", debugstr_w(component));
1072 return ERROR_SUCCESS;
1074 add_feature_component( ilfs->feature, comp );
1075 comp->Enabled = TRUE;
1077 return ERROR_SUCCESS;
1080 static UINT load_feature(MSIRECORD * row, LPVOID param)
1082 static const WCHAR query[] = {
1083 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1084 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1085 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1086 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1087 MSIPACKAGE *package = param;
1088 MSIFEATURE *feature;
1089 MSIQUERY *view;
1090 _ilfs ilfs;
1091 UINT rc;
1093 /* fill in the data */
1095 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1096 if (!feature)
1097 return ERROR_NOT_ENOUGH_MEMORY;
1099 list_init( &feature->Children );
1100 list_init( &feature->Components );
1102 feature->Feature = msi_dup_record_field( row, 1 );
1104 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1106 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1107 feature->Title = msi_dup_record_field( row, 3 );
1108 feature->Description = msi_dup_record_field( row, 4 );
1110 if (!MSI_RecordIsNull(row,5))
1111 feature->Display = MSI_RecordGetInteger(row,5);
1113 feature->Level= MSI_RecordGetInteger(row,6);
1114 feature->Directory = msi_dup_record_field( row, 7 );
1115 feature->Attributes = MSI_RecordGetInteger(row,8);
1117 feature->Installed = INSTALLSTATE_UNKNOWN;
1118 feature->Action = INSTALLSTATE_UNKNOWN;
1119 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1121 list_add_tail( &package->features, &feature->entry );
1123 /* load feature components */
1125 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1126 if (rc != ERROR_SUCCESS)
1127 return ERROR_SUCCESS;
1129 ilfs.package = package;
1130 ilfs.feature = feature;
1132 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1133 msiobj_release(&view->hdr);
1134 return rc;
1137 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1139 MSIPACKAGE *package = param;
1140 MSIFEATURE *parent, *child;
1142 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1143 if (!child)
1144 return ERROR_FUNCTION_FAILED;
1146 if (!child->Feature_Parent)
1147 return ERROR_SUCCESS;
1149 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1150 if (!parent)
1151 return ERROR_FUNCTION_FAILED;
1153 add_feature_child( parent, child );
1154 return ERROR_SUCCESS;
1157 UINT msi_load_all_features( MSIPACKAGE *package )
1159 static const WCHAR query[] = {
1160 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1161 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1162 '`','D','i','s','p','l','a','y','`',0};
1163 MSIQUERY *view;
1164 UINT r;
1166 if (!list_empty(&package->features))
1167 return ERROR_SUCCESS;
1169 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1170 if (r != ERROR_SUCCESS)
1171 return r;
1173 r = MSI_IterateRecords( view, NULL, load_feature, package );
1174 if (r != ERROR_SUCCESS)
1176 msiobj_release( &view->hdr );
1177 return r;
1179 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1180 msiobj_release( &view->hdr );
1181 return r;
1184 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1186 if (!p)
1187 return p;
1188 p = strchrW(p, ch);
1189 if (!p)
1190 return p;
1191 *p = 0;
1192 return p+1;
1195 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1197 static const WCHAR query[] = {
1198 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1199 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1200 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1201 MSIQUERY *view = NULL;
1202 MSIRECORD *row = NULL;
1203 UINT r;
1205 TRACE("%s\n", debugstr_w(file->File));
1207 r = MSI_OpenQuery(package->db, &view, query, file->File);
1208 if (r != ERROR_SUCCESS)
1209 goto done;
1211 r = MSI_ViewExecute(view, NULL);
1212 if (r != ERROR_SUCCESS)
1213 goto done;
1215 r = MSI_ViewFetch(view, &row);
1216 if (r != ERROR_SUCCESS)
1217 goto done;
1219 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1220 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1221 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1222 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1223 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1225 done:
1226 if (view) msiobj_release(&view->hdr);
1227 if (row) msiobj_release(&row->hdr);
1228 return r;
1231 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1233 MSIRECORD *row;
1234 static const WCHAR query[] = {
1235 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1236 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1237 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1239 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1240 if (!row)
1242 WARN("query failed\n");
1243 return ERROR_FUNCTION_FAILED;
1246 file->disk_id = MSI_RecordGetInteger( row, 1 );
1247 msiobj_release( &row->hdr );
1248 return ERROR_SUCCESS;
1251 static UINT load_file(MSIRECORD *row, LPVOID param)
1253 MSIPACKAGE* package = param;
1254 LPCWSTR component;
1255 MSIFILE *file;
1257 /* fill in the data */
1259 file = msi_alloc_zero( sizeof (MSIFILE) );
1260 if (!file)
1261 return ERROR_NOT_ENOUGH_MEMORY;
1263 file->File = msi_dup_record_field( row, 1 );
1265 component = MSI_RecordGetString( row, 2 );
1266 file->Component = msi_get_loaded_component( package, component );
1268 if (!file->Component)
1270 WARN("Component not found: %s\n", debugstr_w(component));
1271 msi_free(file->File);
1272 msi_free(file);
1273 return ERROR_SUCCESS;
1276 file->FileName = msi_dup_record_field( row, 3 );
1277 msi_reduce_to_long_filename( file->FileName );
1279 file->ShortName = msi_dup_record_field( row, 3 );
1280 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1282 file->FileSize = MSI_RecordGetInteger( row, 4 );
1283 file->Version = msi_dup_record_field( row, 5 );
1284 file->Language = msi_dup_record_field( row, 6 );
1285 file->Attributes = MSI_RecordGetInteger( row, 7 );
1286 file->Sequence = MSI_RecordGetInteger( row, 8 );
1288 file->state = msifs_invalid;
1290 /* if the compressed bits are not set in the file attributes,
1291 * then read the information from the package word count property
1293 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1295 file->IsCompressed = FALSE;
1297 else if (file->Attributes &
1298 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1300 file->IsCompressed = TRUE;
1302 else if (file->Attributes & msidbFileAttributesNoncompressed)
1304 file->IsCompressed = FALSE;
1306 else
1308 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1311 load_file_hash(package, file);
1312 load_file_disk_id(package, file);
1314 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1316 list_add_tail( &package->files, &file->entry );
1318 return ERROR_SUCCESS;
1321 static UINT load_all_files(MSIPACKAGE *package)
1323 static const WCHAR query[] = {
1324 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1325 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1326 '`','S','e','q','u','e','n','c','e','`', 0};
1327 MSIQUERY *view;
1328 UINT rc;
1330 if (!list_empty(&package->files))
1331 return ERROR_SUCCESS;
1333 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1334 if (rc != ERROR_SUCCESS)
1335 return ERROR_SUCCESS;
1337 rc = MSI_IterateRecords(view, NULL, load_file, package);
1338 msiobj_release(&view->hdr);
1339 return rc;
1342 static UINT load_media( MSIRECORD *row, LPVOID param )
1344 MSIPACKAGE *package = param;
1345 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1346 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1348 /* FIXME: load external cabinets and directory sources too */
1349 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1350 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1351 return ERROR_SUCCESS;
1354 static UINT load_all_media( MSIPACKAGE *package )
1356 static const WCHAR query[] = {
1357 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1358 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1359 '`','D','i','s','k','I','d','`',0};
1360 MSIQUERY *view;
1361 UINT r;
1363 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1364 if (r != ERROR_SUCCESS)
1365 return ERROR_SUCCESS;
1367 r = MSI_IterateRecords( view, NULL, load_media, package );
1368 msiobj_release( &view->hdr );
1369 return r;
1372 static UINT load_patch(MSIRECORD *row, LPVOID param)
1374 MSIPACKAGE *package = param;
1375 MSIFILEPATCH *patch;
1376 LPWSTR file_key;
1378 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1379 if (!patch)
1380 return ERROR_NOT_ENOUGH_MEMORY;
1382 file_key = msi_dup_record_field( row, 1 );
1383 patch->File = msi_get_loaded_file( package, file_key );
1384 msi_free(file_key);
1386 if( !patch->File )
1388 ERR("Failed to find target for patch in File table\n");
1389 msi_free(patch);
1390 return ERROR_FUNCTION_FAILED;
1393 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1395 /* FIXME: The database should be properly transformed */
1396 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1398 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1399 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1400 patch->IsApplied = FALSE;
1402 /* FIXME:
1403 * Header field - for patch validation.
1404 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1407 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1409 list_add_tail( &package->filepatches, &patch->entry );
1411 return ERROR_SUCCESS;
1414 static UINT load_all_patches(MSIPACKAGE *package)
1416 static const WCHAR query[] = {
1417 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1418 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1419 '`','S','e','q','u','e','n','c','e','`',0};
1420 MSIQUERY *view;
1421 UINT rc;
1423 if (!list_empty(&package->filepatches))
1424 return ERROR_SUCCESS;
1426 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1427 if (rc != ERROR_SUCCESS)
1428 return ERROR_SUCCESS;
1430 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1431 msiobj_release(&view->hdr);
1432 return rc;
1435 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1437 static const WCHAR query[] = {
1438 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1439 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1440 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1441 MSIQUERY *view;
1443 folder->persistent = FALSE;
1444 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1446 if (!MSI_ViewExecute( view, NULL ))
1448 MSIRECORD *rec;
1449 if (!MSI_ViewFetch( view, &rec ))
1451 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1452 folder->persistent = TRUE;
1453 msiobj_release( &rec->hdr );
1456 msiobj_release( &view->hdr );
1458 return ERROR_SUCCESS;
1461 static UINT load_folder( MSIRECORD *row, LPVOID param )
1463 MSIPACKAGE *package = param;
1464 static WCHAR szEmpty[] = { 0 };
1465 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1466 MSIFOLDER *folder;
1468 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1469 list_init( &folder->children );
1470 folder->Directory = msi_dup_record_field( row, 1 );
1471 folder->Parent = msi_dup_record_field( row, 2 );
1472 p = msi_dup_record_field(row, 3);
1474 TRACE("%s\n", debugstr_w(folder->Directory));
1476 /* split src and target dir */
1477 tgt_short = p;
1478 src_short = folder_split_path( p, ':' );
1480 /* split the long and short paths */
1481 tgt_long = folder_split_path( tgt_short, '|' );
1482 src_long = folder_split_path( src_short, '|' );
1484 /* check for no-op dirs */
1485 if (tgt_short && !strcmpW( szDot, tgt_short ))
1486 tgt_short = szEmpty;
1487 if (src_short && !strcmpW( szDot, src_short ))
1488 src_short = szEmpty;
1490 if (!tgt_long)
1491 tgt_long = tgt_short;
1493 if (!src_short) {
1494 src_short = tgt_short;
1495 src_long = tgt_long;
1498 if (!src_long)
1499 src_long = src_short;
1501 /* FIXME: use the target short path too */
1502 folder->TargetDefault = strdupW(tgt_long);
1503 folder->SourceShortPath = strdupW(src_short);
1504 folder->SourceLongPath = strdupW(src_long);
1505 msi_free(p);
1507 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1508 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1509 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1511 load_folder_persistence( package, folder );
1513 list_add_tail( &package->folders, &folder->entry );
1514 return ERROR_SUCCESS;
1517 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1519 FolderList *fl;
1521 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1522 fl->folder = child;
1523 list_add_tail( &parent->children, &fl->entry );
1524 return ERROR_SUCCESS;
1527 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1529 MSIPACKAGE *package = param;
1530 MSIFOLDER *parent, *child;
1532 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1533 return ERROR_FUNCTION_FAILED;
1535 if (!child->Parent) return ERROR_SUCCESS;
1537 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1538 return ERROR_FUNCTION_FAILED;
1540 return add_folder_child( parent, child );
1543 static UINT load_all_folders( MSIPACKAGE *package )
1545 static const WCHAR query[] = {
1546 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1547 '`','D','i','r','e','c','t','o','r','y','`',0};
1548 MSIQUERY *view;
1549 UINT r;
1551 if (!list_empty(&package->folders))
1552 return ERROR_SUCCESS;
1554 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1555 if (r != ERROR_SUCCESS)
1556 return r;
1558 r = MSI_IterateRecords( view, NULL, load_folder, package );
1559 if (r != ERROR_SUCCESS)
1561 msiobj_release( &view->hdr );
1562 return r;
1564 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1565 msiobj_release( &view->hdr );
1566 return r;
1569 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1571 msi_set_property( package->db, szCostingComplete, szZero );
1572 msi_set_property( package->db, szRootDrive, szCRoot );
1574 load_all_folders( package );
1575 msi_load_all_components( package );
1576 msi_load_all_features( package );
1577 load_all_files( package );
1578 load_all_patches( package );
1579 load_all_media( package );
1581 return ERROR_SUCCESS;
1584 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1586 const WCHAR *action = package->script->Actions[script][index];
1587 ui_actionstart( package, action );
1588 TRACE("executing %s\n", debugstr_w(action));
1589 return ACTION_PerformAction( package, action, script );
1592 static UINT execute_script( MSIPACKAGE *package, UINT script )
1594 UINT i, rc = ERROR_SUCCESS;
1596 TRACE("executing script %u\n", script);
1598 if (!package->script)
1600 ERR("no script!\n");
1601 return ERROR_FUNCTION_FAILED;
1603 if (script == SCRIPT_ROLLBACK)
1605 for (i = package->script->ActionCount[script]; i > 0; i--)
1607 rc = execute_script_action( package, script, i - 1 );
1608 if (rc != ERROR_SUCCESS) break;
1611 else
1613 for (i = 0; i < package->script->ActionCount[script]; i++)
1615 rc = execute_script_action( package, script, i );
1616 if (rc != ERROR_SUCCESS) break;
1619 msi_free_action_script(package, script);
1620 return rc;
1623 static UINT ACTION_FileCost(MSIPACKAGE *package)
1625 return ERROR_SUCCESS;
1628 static void get_client_counts( MSIPACKAGE *package )
1630 MSICOMPONENT *comp;
1631 HKEY hkey;
1633 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1635 if (!comp->ComponentId) continue;
1637 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1638 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1640 comp->num_clients = 0;
1641 continue;
1643 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1644 NULL, NULL, NULL, NULL );
1645 RegCloseKey( hkey );
1649 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1651 MSICOMPONENT *comp;
1652 UINT r;
1654 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1656 if (!comp->ComponentId) continue;
1658 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1659 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1660 &comp->Installed );
1661 if (r == ERROR_SUCCESS) continue;
1663 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1664 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1665 &comp->Installed );
1666 if (r == ERROR_SUCCESS) continue;
1668 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1669 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1670 &comp->Installed );
1671 if (r == ERROR_SUCCESS) continue;
1673 comp->Installed = INSTALLSTATE_ABSENT;
1677 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1679 MSIFEATURE *feature;
1681 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1683 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1685 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1686 feature->Installed = INSTALLSTATE_ABSENT;
1687 else
1688 feature->Installed = state;
1692 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1694 return (feature->Level > 0 && feature->Level <= level);
1697 static BOOL process_state_property(MSIPACKAGE* package, int level,
1698 LPCWSTR property, INSTALLSTATE state)
1700 LPWSTR override;
1701 MSIFEATURE *feature;
1703 override = msi_dup_property( package->db, property );
1704 if (!override)
1705 return FALSE;
1707 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1709 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1710 continue;
1712 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1714 if (!strcmpiW( override, szAll ))
1716 if (feature->Installed != state)
1718 feature->Action = state;
1719 feature->ActionRequest = state;
1722 else
1724 LPWSTR ptr = override;
1725 LPWSTR ptr2 = strchrW(override,',');
1727 while (ptr)
1729 int len = ptr2 - ptr;
1731 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1732 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1734 if (feature->Installed != state)
1736 feature->Action = state;
1737 feature->ActionRequest = state;
1739 break;
1741 if (ptr2)
1743 ptr=ptr2+1;
1744 ptr2 = strchrW(ptr,',');
1746 else
1747 break;
1751 msi_free(override);
1752 return TRUE;
1755 static BOOL process_overrides( MSIPACKAGE *package, int level )
1757 static const WCHAR szAddLocal[] =
1758 {'A','D','D','L','O','C','A','L',0};
1759 static const WCHAR szAddSource[] =
1760 {'A','D','D','S','O','U','R','C','E',0};
1761 static const WCHAR szAdvertise[] =
1762 {'A','D','V','E','R','T','I','S','E',0};
1763 BOOL ret = FALSE;
1765 /* all these activation/deactivation things happen in order and things
1766 * later on the list override things earlier on the list.
1768 * 0 INSTALLLEVEL processing
1769 * 1 ADDLOCAL
1770 * 2 REMOVE
1771 * 3 ADDSOURCE
1772 * 4 ADDDEFAULT
1773 * 5 REINSTALL
1774 * 6 ADVERTISE
1775 * 7 COMPADDLOCAL
1776 * 8 COMPADDSOURCE
1777 * 9 FILEADDLOCAL
1778 * 10 FILEADDSOURCE
1779 * 11 FILEADDDEFAULT
1781 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1782 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1783 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1784 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1785 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1787 if (ret)
1788 msi_set_property( package->db, szPreselected, szOne );
1790 return ret;
1793 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1795 int level;
1796 MSICOMPONENT* component;
1797 MSIFEATURE *feature;
1799 TRACE("Checking Install Level\n");
1801 level = msi_get_property_int(package->db, szInstallLevel, 1);
1803 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1805 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1807 if (!is_feature_selected( feature, level )) continue;
1809 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1811 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1813 feature->Action = INSTALLSTATE_SOURCE;
1814 feature->ActionRequest = INSTALLSTATE_SOURCE;
1816 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1818 feature->Action = INSTALLSTATE_ADVERTISED;
1819 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1821 else
1823 feature->Action = INSTALLSTATE_LOCAL;
1824 feature->ActionRequest = INSTALLSTATE_LOCAL;
1828 /* disable child features of unselected parent or follow parent */
1829 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1831 FeatureList *fl;
1833 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1835 if (!is_feature_selected( feature, level ))
1837 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1838 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1840 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1842 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1843 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1844 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1845 fl->feature->Action = feature->Action;
1846 fl->feature->ActionRequest = feature->ActionRequest;
1851 else /* preselected */
1853 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1855 if (!is_feature_selected( feature, level )) continue;
1857 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1859 if (feature->Installed == INSTALLSTATE_ABSENT)
1861 feature->Action = INSTALLSTATE_UNKNOWN;
1862 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1864 else
1866 feature->Action = feature->Installed;
1867 feature->ActionRequest = feature->Installed;
1871 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1873 FeatureList *fl;
1875 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1877 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1878 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1880 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1881 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1882 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1883 fl->feature->Action = feature->Action;
1884 fl->feature->ActionRequest = feature->ActionRequest;
1890 /* now we want to set component state based based on feature state */
1891 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1893 ComponentList *cl;
1895 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1896 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1897 feature->ActionRequest, feature->Action);
1899 if (!is_feature_selected( feature, level )) continue;
1901 /* features with components that have compressed files are made local */
1902 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1904 if (cl->component->ForceLocalState &&
1905 feature->ActionRequest == INSTALLSTATE_SOURCE)
1907 feature->Action = INSTALLSTATE_LOCAL;
1908 feature->ActionRequest = INSTALLSTATE_LOCAL;
1909 break;
1913 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1915 component = cl->component;
1917 switch (feature->ActionRequest)
1919 case INSTALLSTATE_ABSENT:
1920 component->anyAbsent = 1;
1921 break;
1922 case INSTALLSTATE_ADVERTISED:
1923 component->hasAdvertiseFeature = 1;
1924 break;
1925 case INSTALLSTATE_SOURCE:
1926 component->hasSourceFeature = 1;
1927 break;
1928 case INSTALLSTATE_LOCAL:
1929 component->hasLocalFeature = 1;
1930 break;
1931 case INSTALLSTATE_DEFAULT:
1932 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1933 component->hasAdvertiseFeature = 1;
1934 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1935 component->hasSourceFeature = 1;
1936 else
1937 component->hasLocalFeature = 1;
1938 break;
1939 default:
1940 break;
1945 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1947 /* check if it's local or source */
1948 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1949 (component->hasLocalFeature || component->hasSourceFeature))
1951 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1952 !component->ForceLocalState)
1954 component->Action = INSTALLSTATE_SOURCE;
1955 component->ActionRequest = INSTALLSTATE_SOURCE;
1957 else
1959 component->Action = INSTALLSTATE_LOCAL;
1960 component->ActionRequest = INSTALLSTATE_LOCAL;
1962 continue;
1965 /* if any feature is local, the component must be local too */
1966 if (component->hasLocalFeature)
1968 component->Action = INSTALLSTATE_LOCAL;
1969 component->ActionRequest = INSTALLSTATE_LOCAL;
1970 continue;
1972 if (component->hasSourceFeature)
1974 component->Action = INSTALLSTATE_SOURCE;
1975 component->ActionRequest = INSTALLSTATE_SOURCE;
1976 continue;
1978 if (component->hasAdvertiseFeature)
1980 component->Action = INSTALLSTATE_ADVERTISED;
1981 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1982 continue;
1984 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1985 if (component->anyAbsent &&
1986 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1988 component->Action = INSTALLSTATE_ABSENT;
1989 component->ActionRequest = INSTALLSTATE_ABSENT;
1993 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1995 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1997 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1998 component->Action = INSTALLSTATE_LOCAL;
1999 component->ActionRequest = INSTALLSTATE_LOCAL;
2002 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
2003 component->Installed == INSTALLSTATE_SOURCE &&
2004 component->hasSourceFeature)
2006 component->Action = INSTALLSTATE_UNKNOWN;
2007 component->ActionRequest = INSTALLSTATE_UNKNOWN;
2010 TRACE("component %s (installed %d request %d action %d)\n",
2011 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2013 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
2014 component->num_clients++;
2015 else if (component->Action == INSTALLSTATE_ABSENT)
2016 component->num_clients--;
2019 return ERROR_SUCCESS;
2022 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2024 MSIPACKAGE *package = param;
2025 LPCWSTR name;
2026 MSIFEATURE *feature;
2028 name = MSI_RecordGetString( row, 1 );
2030 feature = msi_get_loaded_feature( package, name );
2031 if (!feature)
2032 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2033 else
2035 LPCWSTR Condition;
2036 Condition = MSI_RecordGetString(row,3);
2038 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2040 int level = MSI_RecordGetInteger(row,2);
2041 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2042 feature->Level = level;
2045 return ERROR_SUCCESS;
2048 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2050 static const WCHAR name[] = {'\\',0};
2051 VS_FIXEDFILEINFO *ptr, *ret;
2052 LPVOID version;
2053 DWORD versize, handle;
2054 UINT sz;
2056 versize = GetFileVersionInfoSizeW( filename, &handle );
2057 if (!versize)
2058 return NULL;
2060 version = msi_alloc( versize );
2061 if (!version)
2062 return NULL;
2064 GetFileVersionInfoW( filename, 0, versize, version );
2066 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2068 msi_free( version );
2069 return NULL;
2072 ret = msi_alloc( sz );
2073 memcpy( ret, ptr, sz );
2075 msi_free( version );
2076 return ret;
2079 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2081 DWORD ms, ls;
2083 msi_parse_version_string( version, &ms, &ls );
2085 if (fi->dwFileVersionMS > ms) return 1;
2086 else if (fi->dwFileVersionMS < ms) return -1;
2087 else if (fi->dwFileVersionLS > ls) return 1;
2088 else if (fi->dwFileVersionLS < ls) return -1;
2089 return 0;
2092 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2094 DWORD ms1, ms2;
2096 msi_parse_version_string( ver1, &ms1, NULL );
2097 msi_parse_version_string( ver2, &ms2, NULL );
2099 if (ms1 > ms2) return 1;
2100 else if (ms1 < ms2) return -1;
2101 return 0;
2104 DWORD msi_get_disk_file_size( LPCWSTR filename )
2106 HANDLE file;
2107 DWORD size;
2109 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2110 if (file == INVALID_HANDLE_VALUE)
2111 return INVALID_FILE_SIZE;
2113 size = GetFileSize( file, NULL );
2114 TRACE("size is %u\n", size);
2115 CloseHandle( file );
2116 return size;
2119 BOOL msi_file_hash_matches( MSIFILE *file )
2121 UINT r;
2122 MSIFILEHASHINFO hash;
2124 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2125 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2126 if (r != ERROR_SUCCESS)
2127 return FALSE;
2129 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2132 static WCHAR *get_temp_dir( void )
2134 static UINT id;
2135 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2137 GetTempPathW( MAX_PATH, tmp );
2138 for (;;)
2140 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2141 if (CreateDirectoryW( dir, NULL )) break;
2143 return strdupW( dir );
2147 * msi_build_directory_name()
2149 * This function is to save messing round with directory names
2150 * It handles adding backslashes between path segments,
2151 * and can add \ at the end of the directory name if told to.
2153 * It takes a variable number of arguments.
2154 * It always allocates a new string for the result, so make sure
2155 * to free the return value when finished with it.
2157 * The first arg is the number of path segments that follow.
2158 * The arguments following count are a list of path segments.
2159 * A path segment may be NULL.
2161 * Path segments will be added with a \ separating them.
2162 * A \ will not be added after the last segment, however if the
2163 * last segment is NULL, then the last character will be a \
2165 WCHAR *msi_build_directory_name( DWORD count, ... )
2167 DWORD sz = 1, i;
2168 WCHAR *dir;
2169 va_list va;
2171 va_start( va, count );
2172 for (i = 0; i < count; i++)
2174 const WCHAR *str = va_arg( va, const WCHAR * );
2175 if (str) sz += strlenW( str ) + 1;
2177 va_end( va );
2179 dir = msi_alloc( sz * sizeof(WCHAR) );
2180 dir[0] = 0;
2182 va_start( va, count );
2183 for (i = 0; i < count; i++)
2185 const WCHAR *str = va_arg( va, const WCHAR * );
2186 if (!str) continue;
2187 strcatW( dir, str );
2188 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2190 va_end( va );
2191 return dir;
2194 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2196 MSIASSEMBLY *assembly = file->Component->assembly;
2198 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2200 msi_free( file->TargetPath );
2201 if (assembly && !assembly->application)
2203 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2204 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2205 msi_track_tempfile( package, file->TargetPath );
2207 else
2209 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2210 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2213 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2216 static UINT calculate_file_cost( MSIPACKAGE *package )
2218 VS_FIXEDFILEINFO *file_version;
2219 WCHAR *font_version;
2220 MSIFILE *file;
2222 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2224 MSICOMPONENT *comp = file->Component;
2225 DWORD file_size;
2227 if (!comp->Enabled) continue;
2229 if (file->IsCompressed)
2230 comp->ForceLocalState = TRUE;
2232 set_target_path( package, file );
2234 if ((comp->assembly && !comp->assembly->installed) ||
2235 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2237 comp->Cost += file->FileSize;
2238 continue;
2240 file_size = msi_get_disk_file_size( file->TargetPath );
2242 if (file->Version)
2244 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2246 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2248 comp->Cost += file->FileSize - file_size;
2250 msi_free( file_version );
2251 continue;
2253 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2255 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2257 comp->Cost += file->FileSize - file_size;
2259 msi_free( font_version );
2260 continue;
2263 if (file_size != file->FileSize)
2265 comp->Cost += file->FileSize - file_size;
2268 return ERROR_SUCCESS;
2271 WCHAR *msi_normalize_path( const WCHAR *in )
2273 const WCHAR *p = in;
2274 WCHAR *q, *ret;
2275 int n, len = strlenW( in ) + 2;
2277 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2279 len = 0;
2280 while (1)
2282 /* copy until the end of the string or a space */
2283 while (*p != ' ' && (*q = *p))
2285 p++, len++;
2286 /* reduce many backslashes to one */
2287 if (*p != '\\' || *q != '\\')
2288 q++;
2291 /* quit at the end of the string */
2292 if (!*p)
2293 break;
2295 /* count the number of spaces */
2296 n = 0;
2297 while (p[n] == ' ')
2298 n++;
2300 /* if it's leading or trailing space, skip it */
2301 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2302 p += n;
2303 else /* copy n spaces */
2304 while (n && (*q++ = *p++)) n--;
2306 while (q - ret > 0 && q[-1] == ' ') q--;
2307 if (q - ret > 0 && q[-1] != '\\')
2309 q[0] = '\\';
2310 q[1] = 0;
2312 return ret;
2315 static WCHAR *get_install_location( MSIPACKAGE *package )
2317 HKEY hkey;
2318 WCHAR *path;
2320 if (!package->ProductCode) return NULL;
2321 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE ))
2322 return NULL;
2323 path = msi_reg_get_val_str( hkey, szInstallLocation );
2324 RegCloseKey( hkey );
2325 return path;
2328 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2330 FolderList *fl;
2331 MSIFOLDER *folder, *parent, *child;
2332 WCHAR *path, *normalized_path;
2334 TRACE("resolving %s\n", debugstr_w(name));
2336 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2338 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2340 if (!(path = get_install_location( package )) &&
2341 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2343 path = msi_dup_property( package->db, szRootDrive );
2346 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2348 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2350 parent = msi_get_loaded_folder( package, folder->Parent );
2351 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2353 else
2354 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2356 normalized_path = msi_normalize_path( path );
2357 msi_free( path );
2358 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2360 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2361 msi_free( normalized_path );
2362 return;
2364 msi_set_property( package->db, folder->Directory, normalized_path );
2365 msi_free( folder->ResolvedTarget );
2366 folder->ResolvedTarget = normalized_path;
2368 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2370 child = fl->folder;
2371 msi_resolve_target_folder( package, child->Directory, load_prop );
2373 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2376 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2378 static const WCHAR query[] = {
2379 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2380 '`','C','o','n','d','i','t','i','o','n','`',0};
2381 static const WCHAR szOutOfDiskSpace[] = {
2382 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2383 MSICOMPONENT *comp;
2384 MSIQUERY *view;
2385 LPWSTR level;
2386 UINT rc;
2388 TRACE("Building directory properties\n");
2389 msi_resolve_target_folder( package, szTargetDir, TRUE );
2391 TRACE("Evaluating component conditions\n");
2392 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2394 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2396 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2397 comp->Enabled = FALSE;
2399 else
2400 comp->Enabled = TRUE;
2402 get_client_counts( package );
2404 /* read components states from the registry */
2405 ACTION_GetComponentInstallStates(package);
2406 ACTION_GetFeatureInstallStates(package);
2408 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2410 TRACE("Evaluating feature conditions\n");
2412 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2413 if (rc == ERROR_SUCCESS)
2415 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2416 msiobj_release( &view->hdr );
2417 if (rc != ERROR_SUCCESS)
2418 return rc;
2422 TRACE("Calculating file cost\n");
2423 calculate_file_cost( package );
2425 msi_set_property( package->db, szCostingComplete, szOne );
2426 /* set default run level if not set */
2427 level = msi_dup_property( package->db, szInstallLevel );
2428 if (!level)
2429 msi_set_property( package->db, szInstallLevel, szOne );
2430 msi_free(level);
2432 /* FIXME: check volume disk space */
2433 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2435 return MSI_SetFeatureStates(package);
2438 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, DWORD *size)
2440 LPSTR data = NULL;
2442 if (!value)
2444 data = (LPSTR)strdupW(szEmpty);
2445 *size = sizeof(szEmpty);
2446 *type = REG_SZ;
2447 return data;
2449 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2451 if (value[1]=='x')
2453 LPWSTR ptr;
2454 CHAR byte[5];
2455 LPWSTR deformated = NULL;
2456 int count;
2458 deformat_string(package, &value[2], &deformated);
2460 /* binary value type */
2461 ptr = deformated;
2462 *type = REG_BINARY;
2463 if (strlenW(ptr)%2)
2464 *size = (strlenW(ptr)/2)+1;
2465 else
2466 *size = strlenW(ptr)/2;
2468 data = msi_alloc(*size);
2470 byte[0] = '0';
2471 byte[1] = 'x';
2472 byte[4] = 0;
2473 count = 0;
2474 /* if uneven pad with a zero in front */
2475 if (strlenW(ptr)%2)
2477 byte[2]= '0';
2478 byte[3]= *ptr;
2479 ptr++;
2480 data[count] = (BYTE)strtol(byte,NULL,0);
2481 count ++;
2482 TRACE("Uneven byte count\n");
2484 while (*ptr)
2486 byte[2]= *ptr;
2487 ptr++;
2488 byte[3]= *ptr;
2489 ptr++;
2490 data[count] = (BYTE)strtol(byte,NULL,0);
2491 count ++;
2493 msi_free(deformated);
2495 TRACE("Data %i bytes(%i)\n",*size,count);
2497 else
2499 LPWSTR deformated;
2500 LPWSTR p;
2501 DWORD d = 0;
2502 deformat_string(package, &value[1], &deformated);
2504 *type=REG_DWORD;
2505 *size = sizeof(DWORD);
2506 data = msi_alloc(*size);
2507 p = deformated;
2508 if (*p == '-')
2509 p++;
2510 while (*p)
2512 if ( (*p < '0') || (*p > '9') )
2513 break;
2514 d *= 10;
2515 d += (*p - '0');
2516 p++;
2518 if (deformated[0] == '-')
2519 d = -d;
2520 *(LPDWORD)data = d;
2521 TRACE("DWORD %i\n",*(LPDWORD)data);
2523 msi_free(deformated);
2526 else
2528 static const WCHAR szMulti[] = {'[','~',']',0};
2529 LPCWSTR ptr;
2530 *type=REG_SZ;
2532 if (value[0]=='#')
2534 if (value[1]=='%')
2536 ptr = &value[2];
2537 *type=REG_EXPAND_SZ;
2539 else
2540 ptr = &value[1];
2542 else
2543 ptr=value;
2545 if (strstrW(value, szMulti))
2546 *type = REG_MULTI_SZ;
2548 /* remove initial delimiter */
2549 if (!strncmpW(value, szMulti, 3))
2550 ptr = value + 3;
2552 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2554 /* add double NULL terminator */
2555 if (*type == REG_MULTI_SZ)
2557 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2558 data = msi_realloc_zero(data, *size);
2561 return data;
2564 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2566 const WCHAR *ret;
2568 switch (root)
2570 case -1:
2571 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2573 *root_key = HKEY_LOCAL_MACHINE;
2574 ret = szHLM;
2576 else
2578 *root_key = HKEY_CURRENT_USER;
2579 ret = szHCU;
2581 break;
2582 case 0:
2583 *root_key = HKEY_CLASSES_ROOT;
2584 ret = szHCR;
2585 break;
2586 case 1:
2587 *root_key = HKEY_CURRENT_USER;
2588 ret = szHCU;
2589 break;
2590 case 2:
2591 *root_key = HKEY_LOCAL_MACHINE;
2592 ret = szHLM;
2593 break;
2594 case 3:
2595 *root_key = HKEY_USERS;
2596 ret = szHU;
2597 break;
2598 default:
2599 ERR("Unknown root %i\n", root);
2600 return NULL;
2603 return ret;
2606 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2608 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2609 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2611 if ((is_64bit || is_wow64) &&
2612 !(comp->Attributes & msidbComponentAttributes64bit) &&
2613 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2615 UINT size;
2616 WCHAR *path_32node;
2618 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2619 if (!(path_32node = msi_alloc( size ))) return NULL;
2621 memcpy( path_32node, path, len * sizeof(WCHAR) );
2622 strcpyW( path_32node + len, szWow6432Node );
2623 strcatW( path_32node, szBackSlash );
2624 strcatW( path_32node, path + len );
2625 return path_32node;
2627 return strdupW( path );
2630 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2632 REGSAM access = KEY_ALL_ACCESS;
2633 WCHAR *subkey, *p, *q;
2634 HKEY hkey, ret = NULL;
2635 LONG res;
2637 if (is_wow64) access |= KEY_WOW64_64KEY;
2639 if (!(subkey = strdupW( path ))) return NULL;
2640 p = subkey;
2641 if ((q = strchrW( p, '\\' ))) *q = 0;
2642 if (create)
2643 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2644 else
2645 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2646 if (res)
2648 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2649 msi_free( subkey );
2650 return NULL;
2652 if (q && q[1])
2654 ret = open_key( hkey, q + 1, create );
2655 RegCloseKey( hkey );
2657 else ret = hkey;
2658 msi_free( subkey );
2659 return ret;
2662 static BOOL is_special_entry( const WCHAR *name )
2664 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2667 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2669 MSIPACKAGE *package = param;
2670 LPSTR value;
2671 HKEY root_key, hkey;
2672 DWORD type,size;
2673 LPWSTR deformated, uikey, keypath;
2674 LPCWSTR szRoot, component, name, key;
2675 MSICOMPONENT *comp;
2676 MSIRECORD * uirow;
2677 INT root;
2678 BOOL check_first = FALSE;
2679 UINT rc;
2681 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2683 component = MSI_RecordGetString(row, 6);
2684 comp = msi_get_loaded_component(package,component);
2685 if (!comp)
2686 return ERROR_SUCCESS;
2688 comp->Action = msi_get_component_action( package, comp );
2689 if (comp->Action != INSTALLSTATE_LOCAL)
2691 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2692 return ERROR_SUCCESS;
2695 name = MSI_RecordGetString(row, 4);
2696 if( MSI_RecordIsNull(row,5) && name )
2698 /* null values can have special meanings */
2699 if (name[0]=='-' && name[1] == 0)
2700 return ERROR_SUCCESS;
2701 if ((name[0] == '+' || name[0] == '*') && !name[1])
2702 check_first = TRUE;
2705 root = MSI_RecordGetInteger(row,2);
2706 key = MSI_RecordGetString(row, 3);
2708 szRoot = get_root_key( package, root, &root_key );
2709 if (!szRoot)
2710 return ERROR_SUCCESS;
2712 deformat_string(package, key , &deformated);
2713 size = strlenW(deformated) + strlenW(szRoot) + 1;
2714 uikey = msi_alloc(size*sizeof(WCHAR));
2715 strcpyW(uikey,szRoot);
2716 strcatW(uikey,deformated);
2718 keypath = get_keypath( comp, root_key, deformated );
2719 msi_free( deformated );
2720 if (!(hkey = open_key( root_key, keypath, TRUE )))
2722 ERR("Could not create key %s\n", debugstr_w(keypath));
2723 msi_free(uikey);
2724 msi_free(keypath);
2725 return ERROR_FUNCTION_FAILED;
2727 value = parse_value(package, MSI_RecordGetString(row, 5), &type, &size);
2728 deformat_string(package, name, &deformated);
2730 if (!is_special_entry( name ))
2732 if (!check_first)
2734 TRACE("Setting value %s of %s\n", debugstr_w(deformated),
2735 debugstr_w(uikey));
2736 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2738 else
2740 DWORD sz = 0;
2741 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2742 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2744 TRACE("value %s of %s checked already exists\n", debugstr_w(deformated),
2745 debugstr_w(uikey));
2747 else
2749 TRACE("Checked and setting value %s of %s\n", debugstr_w(deformated),
2750 debugstr_w(uikey));
2751 if (deformated || size)
2752 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2756 RegCloseKey(hkey);
2758 uirow = MSI_CreateRecord(3);
2759 MSI_RecordSetStringW(uirow,2,deformated);
2760 MSI_RecordSetStringW(uirow,1,uikey);
2761 if (type == REG_SZ || type == REG_EXPAND_SZ)
2762 MSI_RecordSetStringW(uirow, 3, (LPWSTR)value);
2763 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2764 msiobj_release( &uirow->hdr );
2766 msi_free(value);
2767 msi_free(deformated);
2768 msi_free(uikey);
2769 msi_free(keypath);
2771 return ERROR_SUCCESS;
2774 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2776 static const WCHAR query[] = {
2777 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2778 '`','R','e','g','i','s','t','r','y','`',0};
2779 MSIQUERY *view;
2780 UINT rc;
2782 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2783 if (rc != ERROR_SUCCESS)
2784 return ERROR_SUCCESS;
2786 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2787 msiobj_release(&view->hdr);
2788 return rc;
2791 static void delete_key( HKEY root, const WCHAR *path )
2793 REGSAM access = 0;
2794 WCHAR *subkey, *p;
2795 HKEY hkey;
2796 LONG res;
2798 if (is_wow64) access |= KEY_WOW64_64KEY;
2800 if (!(subkey = strdupW( path ))) return;
2801 for (;;)
2803 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2804 hkey = open_key( root, subkey, FALSE );
2805 if (!hkey) break;
2806 if (p && p[1])
2807 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2808 else
2809 res = RegDeleteKeyExW( root, subkey, access, 0 );
2810 if (res)
2812 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2813 break;
2815 if (p && p[1]) RegCloseKey( hkey );
2816 else break;
2818 msi_free( subkey );
2821 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2823 LONG res;
2824 HKEY hkey;
2825 DWORD num_subkeys, num_values;
2827 if ((hkey = open_key( root, path, FALSE )))
2829 if ((res = RegDeleteValueW( hkey, value )))
2830 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2832 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2833 NULL, NULL, NULL, NULL );
2834 RegCloseKey( hkey );
2835 if (!res && !num_subkeys && !num_values)
2837 TRACE("removing empty key %s\n", debugstr_w(path));
2838 delete_key( root, path );
2843 static void delete_tree( HKEY root, const WCHAR *path )
2845 LONG res;
2846 HKEY hkey;
2848 if (!(hkey = open_key( root, path, FALSE ))) return;
2849 res = RegDeleteTreeW( hkey, NULL );
2850 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
2851 delete_key( root, path );
2852 RegCloseKey( hkey );
2855 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2857 MSIPACKAGE *package = param;
2858 LPCWSTR component, name, key_str, root_key_str;
2859 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2860 MSICOMPONENT *comp;
2861 MSIRECORD *uirow;
2862 BOOL delete_key = FALSE;
2863 HKEY hkey_root;
2864 UINT size;
2865 INT root;
2867 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2869 component = MSI_RecordGetString( row, 6 );
2870 comp = msi_get_loaded_component( package, component );
2871 if (!comp)
2872 return ERROR_SUCCESS;
2874 comp->Action = msi_get_component_action( package, comp );
2875 if (comp->Action != INSTALLSTATE_ABSENT)
2877 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2878 return ERROR_SUCCESS;
2881 name = MSI_RecordGetString( row, 4 );
2882 if (MSI_RecordIsNull( row, 5 ) && name )
2884 if (name[0] == '+' && !name[1])
2885 return ERROR_SUCCESS;
2886 if ((name[0] == '-' || name[0] == '*') && !name[1])
2888 delete_key = TRUE;
2889 name = NULL;
2893 root = MSI_RecordGetInteger( row, 2 );
2894 key_str = MSI_RecordGetString( row, 3 );
2896 root_key_str = get_root_key( package, root, &hkey_root );
2897 if (!root_key_str)
2898 return ERROR_SUCCESS;
2900 deformat_string( package, key_str, &deformated_key );
2901 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2902 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2903 strcpyW( ui_key_str, root_key_str );
2904 strcatW( ui_key_str, deformated_key );
2906 deformat_string( package, name, &deformated_name );
2908 keypath = get_keypath( comp, hkey_root, deformated_key );
2909 msi_free( deformated_key );
2910 if (delete_key) delete_tree( hkey_root, keypath );
2911 else delete_value( hkey_root, keypath, deformated_name );
2912 msi_free( keypath );
2914 uirow = MSI_CreateRecord( 2 );
2915 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2916 MSI_RecordSetStringW( uirow, 2, deformated_name );
2917 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2918 msiobj_release( &uirow->hdr );
2920 msi_free( ui_key_str );
2921 msi_free( deformated_name );
2922 return ERROR_SUCCESS;
2925 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2927 MSIPACKAGE *package = param;
2928 LPCWSTR component, name, key_str, root_key_str;
2929 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2930 MSICOMPONENT *comp;
2931 MSIRECORD *uirow;
2932 BOOL delete_key = FALSE;
2933 HKEY hkey_root;
2934 UINT size;
2935 INT root;
2937 component = MSI_RecordGetString( row, 5 );
2938 comp = msi_get_loaded_component( package, component );
2939 if (!comp)
2940 return ERROR_SUCCESS;
2942 comp->Action = msi_get_component_action( package, comp );
2943 if (comp->Action != INSTALLSTATE_LOCAL)
2945 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2946 return ERROR_SUCCESS;
2949 if ((name = MSI_RecordGetString( row, 4 )))
2951 if (name[0] == '-' && !name[1])
2953 delete_key = TRUE;
2954 name = NULL;
2958 root = MSI_RecordGetInteger( row, 2 );
2959 key_str = MSI_RecordGetString( row, 3 );
2961 root_key_str = get_root_key( package, root, &hkey_root );
2962 if (!root_key_str)
2963 return ERROR_SUCCESS;
2965 deformat_string( package, key_str, &deformated_key );
2966 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2967 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2968 strcpyW( ui_key_str, root_key_str );
2969 strcatW( ui_key_str, deformated_key );
2971 deformat_string( package, name, &deformated_name );
2973 keypath = get_keypath( comp, hkey_root, deformated_key );
2974 msi_free( deformated_key );
2975 if (delete_key) delete_tree( hkey_root, keypath );
2976 else delete_value( hkey_root, keypath, deformated_name );
2977 msi_free( keypath );
2979 uirow = MSI_CreateRecord( 2 );
2980 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2981 MSI_RecordSetStringW( uirow, 2, deformated_name );
2982 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2983 msiobj_release( &uirow->hdr );
2985 msi_free( ui_key_str );
2986 msi_free( deformated_name );
2987 return ERROR_SUCCESS;
2990 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2992 static const WCHAR registry_query[] = {
2993 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2994 '`','R','e','g','i','s','t','r','y','`',0};
2995 static const WCHAR remove_registry_query[] = {
2996 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2997 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2998 MSIQUERY *view;
2999 UINT rc;
3001 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3002 if (rc == ERROR_SUCCESS)
3004 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3005 msiobj_release( &view->hdr );
3006 if (rc != ERROR_SUCCESS)
3007 return rc;
3009 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3010 if (rc == ERROR_SUCCESS)
3012 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3013 msiobj_release( &view->hdr );
3014 if (rc != ERROR_SUCCESS)
3015 return rc;
3017 return ERROR_SUCCESS;
3020 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3022 package->script->CurrentlyScripting = TRUE;
3024 return ERROR_SUCCESS;
3028 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3030 static const WCHAR query[]= {
3031 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3032 '`','R','e','g','i','s','t','r','y','`',0};
3033 MSICOMPONENT *comp;
3034 DWORD total = 0, count = 0;
3035 MSIQUERY *view;
3036 MSIFEATURE *feature;
3037 MSIFILE *file;
3038 UINT rc;
3040 TRACE("InstallValidate\n");
3042 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3043 if (rc == ERROR_SUCCESS)
3045 rc = MSI_IterateRecords( view, &count, NULL, package );
3046 msiobj_release( &view->hdr );
3047 if (rc != ERROR_SUCCESS)
3048 return rc;
3049 total += count * REG_PROGRESS_VALUE;
3051 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3052 total += COMPONENT_PROGRESS_VALUE;
3054 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3055 total += file->FileSize;
3057 msi_ui_progress( package, 0, total, 0, 0 );
3059 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3061 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3062 debugstr_w(feature->Feature), feature->Installed,
3063 feature->ActionRequest, feature->Action);
3065 return ERROR_SUCCESS;
3068 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3070 MSIPACKAGE* package = param;
3071 LPCWSTR cond = NULL;
3072 LPCWSTR message = NULL;
3073 UINT r;
3075 static const WCHAR title[]=
3076 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3078 cond = MSI_RecordGetString(row,1);
3080 r = MSI_EvaluateConditionW(package,cond);
3081 if (r == MSICONDITION_FALSE)
3083 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3085 LPWSTR deformated;
3086 message = MSI_RecordGetString(row,2);
3087 deformat_string(package,message,&deformated);
3088 MessageBoxW(NULL,deformated,title,MB_OK);
3089 msi_free(deformated);
3092 return ERROR_INSTALL_FAILURE;
3095 return ERROR_SUCCESS;
3098 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3100 static const WCHAR query[] = {
3101 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3102 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3103 MSIQUERY *view;
3104 UINT rc;
3106 TRACE("Checking launch conditions\n");
3108 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3109 if (rc != ERROR_SUCCESS)
3110 return ERROR_SUCCESS;
3112 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3113 msiobj_release(&view->hdr);
3114 return rc;
3117 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3120 if (!cmp->KeyPath)
3121 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3123 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3125 static const WCHAR query[] = {
3126 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3127 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3128 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3129 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3130 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3131 MSIRECORD *row;
3132 UINT root, len;
3133 LPWSTR deformated, buffer, deformated_name;
3134 LPCWSTR key, name;
3136 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3137 if (!row)
3138 return NULL;
3140 root = MSI_RecordGetInteger(row,2);
3141 key = MSI_RecordGetString(row, 3);
3142 name = MSI_RecordGetString(row, 4);
3143 deformat_string(package, key , &deformated);
3144 deformat_string(package, name, &deformated_name);
3146 len = strlenW(deformated) + 6;
3147 if (deformated_name)
3148 len+=strlenW(deformated_name);
3150 buffer = msi_alloc( len *sizeof(WCHAR));
3152 if (deformated_name)
3153 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3154 else
3155 sprintfW(buffer,fmt,root,deformated);
3157 msi_free(deformated);
3158 msi_free(deformated_name);
3159 msiobj_release(&row->hdr);
3161 return buffer;
3163 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3165 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3166 return NULL;
3168 else
3170 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3172 if (file)
3173 return strdupW( file->TargetPath );
3175 return NULL;
3178 static HKEY openSharedDLLsKey(void)
3180 HKEY hkey=0;
3181 static const WCHAR path[] =
3182 {'S','o','f','t','w','a','r','e','\\',
3183 'M','i','c','r','o','s','o','f','t','\\',
3184 'W','i','n','d','o','w','s','\\',
3185 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3186 'S','h','a','r','e','d','D','L','L','s',0};
3188 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3189 return hkey;
3192 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3194 HKEY hkey;
3195 DWORD count=0;
3196 DWORD type;
3197 DWORD sz = sizeof(count);
3198 DWORD rc;
3200 hkey = openSharedDLLsKey();
3201 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3202 if (rc != ERROR_SUCCESS)
3203 count = 0;
3204 RegCloseKey(hkey);
3205 return count;
3208 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3210 HKEY hkey;
3212 hkey = openSharedDLLsKey();
3213 if (count > 0)
3214 msi_reg_set_val_dword( hkey, path, count );
3215 else
3216 RegDeleteValueW(hkey,path);
3217 RegCloseKey(hkey);
3218 return count;
3221 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3223 MSIFEATURE *feature;
3224 INT count = 0;
3225 BOOL write = FALSE;
3227 /* only refcount DLLs */
3228 if (comp->KeyPath == NULL ||
3229 comp->assembly ||
3230 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3231 comp->Attributes & msidbComponentAttributesODBCDataSource)
3232 write = FALSE;
3233 else
3235 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3236 write = (count > 0);
3238 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3239 write = TRUE;
3242 /* increment counts */
3243 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3245 ComponentList *cl;
3247 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3248 continue;
3250 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3252 if ( cl->component == comp )
3253 count++;
3257 /* decrement counts */
3258 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3260 ComponentList *cl;
3262 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3263 continue;
3265 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3267 if ( cl->component == comp )
3268 count--;
3272 /* ref count all the files in the component */
3273 if (write)
3275 MSIFILE *file;
3277 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3279 if (file->Component == comp)
3280 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3284 /* add a count for permanent */
3285 if (comp->Attributes & msidbComponentAttributesPermanent)
3286 count ++;
3288 comp->RefCount = count;
3290 if (write)
3291 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3294 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3296 if (comp->assembly)
3298 const WCHAR prefixW[] = {'<','\\',0};
3299 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3300 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3302 if (keypath)
3304 strcpyW( keypath, prefixW );
3305 strcatW( keypath, comp->assembly->display_name );
3307 return keypath;
3309 return resolve_keypath( package, comp );
3312 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3314 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3315 UINT rc;
3316 MSICOMPONENT *comp;
3317 HKEY hkey;
3319 TRACE("\n");
3321 squash_guid(package->ProductCode,squished_pc);
3322 msi_set_sourcedir_props(package, FALSE);
3324 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3326 MSIRECORD *uirow;
3327 INSTALLSTATE action;
3329 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3330 if (!comp->ComponentId)
3331 continue;
3333 squash_guid( comp->ComponentId, squished_cc );
3334 msi_free( comp->FullKeypath );
3335 comp->FullKeypath = build_full_keypath( package, comp );
3337 ACTION_RefCountComponent( package, comp );
3339 if (package->need_rollback) action = comp->Installed;
3340 else action = comp->ActionRequest;
3342 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3343 debugstr_w(comp->Component), debugstr_w(squished_cc),
3344 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3346 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3348 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3349 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3350 else
3351 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3353 if (rc != ERROR_SUCCESS)
3354 continue;
3356 if (comp->Attributes & msidbComponentAttributesPermanent)
3358 static const WCHAR szPermKey[] =
3359 { '0','0','0','0','0','0','0','0','0','0','0','0',
3360 '0','0','0','0','0','0','0','0','0','0','0','0',
3361 '0','0','0','0','0','0','0','0',0 };
3363 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3365 if (action == INSTALLSTATE_LOCAL)
3366 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3367 else
3369 MSIFILE *file;
3370 MSIRECORD *row;
3371 LPWSTR ptr, ptr2;
3372 WCHAR source[MAX_PATH];
3373 WCHAR base[MAX_PATH];
3374 LPWSTR sourcepath;
3376 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3377 static const WCHAR query[] = {
3378 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3379 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3380 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3381 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3382 '`','D','i','s','k','I','d','`',0};
3384 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3385 continue;
3387 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3388 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3389 ptr2 = strrchrW(source, '\\') + 1;
3390 msiobj_release(&row->hdr);
3392 lstrcpyW(base, package->PackagePath);
3393 ptr = strrchrW(base, '\\');
3394 *(ptr + 1) = '\0';
3396 sourcepath = msi_resolve_file_source(package, file);
3397 ptr = sourcepath + lstrlenW(base);
3398 lstrcpyW(ptr2, ptr);
3399 msi_free(sourcepath);
3401 msi_reg_set_val_str(hkey, squished_pc, source);
3403 RegCloseKey(hkey);
3405 else if (action == INSTALLSTATE_ABSENT)
3407 if (comp->num_clients <= 0)
3409 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3410 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3411 else
3412 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3416 /* UI stuff */
3417 uirow = MSI_CreateRecord(3);
3418 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3419 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3420 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3421 msi_ui_actiondata( package, szProcessComponents, uirow );
3422 msiobj_release( &uirow->hdr );
3424 return ERROR_SUCCESS;
3427 typedef struct {
3428 CLSID clsid;
3429 LPWSTR source;
3431 LPWSTR path;
3432 ITypeLib *ptLib;
3433 } typelib_struct;
3435 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3436 LPWSTR lpszName, LONG_PTR lParam)
3438 TLIBATTR *attr;
3439 typelib_struct *tl_struct = (typelib_struct*) lParam;
3440 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3441 int sz;
3442 HRESULT res;
3444 if (!IS_INTRESOURCE(lpszName))
3446 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3447 return TRUE;
3450 sz = strlenW(tl_struct->source)+4;
3451 sz *= sizeof(WCHAR);
3453 if ((INT_PTR)lpszName == 1)
3454 tl_struct->path = strdupW(tl_struct->source);
3455 else
3457 tl_struct->path = msi_alloc(sz);
3458 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3461 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3462 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3463 if (FAILED(res))
3465 msi_free(tl_struct->path);
3466 tl_struct->path = NULL;
3468 return TRUE;
3471 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3472 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3474 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3475 return FALSE;
3478 msi_free(tl_struct->path);
3479 tl_struct->path = NULL;
3481 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3482 ITypeLib_Release(tl_struct->ptLib);
3484 return TRUE;
3487 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3489 MSIPACKAGE* package = param;
3490 LPCWSTR component;
3491 MSICOMPONENT *comp;
3492 MSIFILE *file;
3493 typelib_struct tl_struct;
3494 ITypeLib *tlib;
3495 HMODULE module;
3496 HRESULT hr;
3498 component = MSI_RecordGetString(row,3);
3499 comp = msi_get_loaded_component(package,component);
3500 if (!comp)
3501 return ERROR_SUCCESS;
3503 comp->Action = msi_get_component_action( package, comp );
3504 if (comp->Action != INSTALLSTATE_LOCAL)
3506 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3507 return ERROR_SUCCESS;
3510 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3512 TRACE("component has no key path\n");
3513 return ERROR_SUCCESS;
3515 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3517 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3518 if (module)
3520 LPCWSTR guid;
3521 guid = MSI_RecordGetString(row,1);
3522 CLSIDFromString( guid, &tl_struct.clsid);
3523 tl_struct.source = strdupW( file->TargetPath );
3524 tl_struct.path = NULL;
3526 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3527 (LONG_PTR)&tl_struct);
3529 if (tl_struct.path)
3531 LPCWSTR helpid, help_path = NULL;
3532 HRESULT res;
3534 helpid = MSI_RecordGetString(row,6);
3536 if (helpid) help_path = msi_get_target_folder( package, helpid );
3537 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3539 if (FAILED(res))
3540 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3541 else
3542 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3544 ITypeLib_Release(tl_struct.ptLib);
3545 msi_free(tl_struct.path);
3547 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3549 FreeLibrary(module);
3550 msi_free(tl_struct.source);
3552 else
3554 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3555 if (FAILED(hr))
3557 ERR("Failed to load type library: %08x\n", hr);
3558 return ERROR_INSTALL_FAILURE;
3561 ITypeLib_Release(tlib);
3564 return ERROR_SUCCESS;
3567 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3569 static const WCHAR query[] = {
3570 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3571 '`','T','y','p','e','L','i','b','`',0};
3572 MSIQUERY *view;
3573 UINT rc;
3575 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3576 if (rc != ERROR_SUCCESS)
3577 return ERROR_SUCCESS;
3579 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3580 msiobj_release(&view->hdr);
3581 return rc;
3584 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3586 MSIPACKAGE *package = param;
3587 LPCWSTR component, guid;
3588 MSICOMPONENT *comp;
3589 GUID libid;
3590 UINT version;
3591 LCID language;
3592 SYSKIND syskind;
3593 HRESULT hr;
3595 component = MSI_RecordGetString( row, 3 );
3596 comp = msi_get_loaded_component( package, component );
3597 if (!comp)
3598 return ERROR_SUCCESS;
3600 comp->Action = msi_get_component_action( package, comp );
3601 if (comp->Action != INSTALLSTATE_ABSENT)
3603 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3604 return ERROR_SUCCESS;
3606 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3608 guid = MSI_RecordGetString( row, 1 );
3609 CLSIDFromString( guid, &libid );
3610 version = MSI_RecordGetInteger( row, 4 );
3611 language = MSI_RecordGetInteger( row, 2 );
3613 #ifdef _WIN64
3614 syskind = SYS_WIN64;
3615 #else
3616 syskind = SYS_WIN32;
3617 #endif
3619 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3620 if (FAILED(hr))
3622 WARN("Failed to unregister typelib: %08x\n", hr);
3625 return ERROR_SUCCESS;
3628 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3630 static const WCHAR query[] = {
3631 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3632 '`','T','y','p','e','L','i','b','`',0};
3633 MSIQUERY *view;
3634 UINT rc;
3636 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3637 if (rc != ERROR_SUCCESS)
3638 return ERROR_SUCCESS;
3640 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3641 msiobj_release( &view->hdr );
3642 return rc;
3645 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3647 static const WCHAR szlnk[] = {'.','l','n','k',0};
3648 LPCWSTR directory, extension, link_folder;
3649 LPWSTR link_file, filename;
3651 directory = MSI_RecordGetString( row, 2 );
3652 link_folder = msi_get_target_folder( package, directory );
3653 if (!link_folder)
3655 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3656 return NULL;
3658 /* may be needed because of a bug somewhere else */
3659 msi_create_full_path( link_folder );
3661 filename = msi_dup_record_field( row, 3 );
3662 msi_reduce_to_long_filename( filename );
3664 extension = strchrW( filename, '.' );
3665 if (!extension || strcmpiW( extension, szlnk ))
3667 int len = strlenW( filename );
3668 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3669 memcpy( filename + len, szlnk, sizeof(szlnk) );
3671 link_file = msi_build_directory_name( 2, link_folder, filename );
3672 msi_free( filename );
3674 return link_file;
3677 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3679 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3680 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3681 WCHAR *folder, *dest, *path;
3683 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3684 folder = msi_dup_property( package->db, szWindowsFolder );
3685 else
3687 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3688 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3689 msi_free( appdata );
3691 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3692 msi_create_full_path( dest );
3693 path = msi_build_directory_name( 2, dest, icon_name );
3694 msi_free( folder );
3695 msi_free( dest );
3696 return path;
3699 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3701 MSIPACKAGE *package = param;
3702 LPWSTR link_file, deformated, path;
3703 LPCWSTR component, target;
3704 MSICOMPONENT *comp;
3705 IShellLinkW *sl = NULL;
3706 IPersistFile *pf = NULL;
3707 HRESULT res;
3709 component = MSI_RecordGetString(row, 4);
3710 comp = msi_get_loaded_component(package, component);
3711 if (!comp)
3712 return ERROR_SUCCESS;
3714 comp->Action = msi_get_component_action( package, comp );
3715 if (comp->Action != INSTALLSTATE_LOCAL)
3717 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3718 return ERROR_SUCCESS;
3720 msi_ui_actiondata( package, szCreateShortcuts, row );
3722 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3723 &IID_IShellLinkW, (LPVOID *) &sl );
3725 if (FAILED( res ))
3727 ERR("CLSID_ShellLink not available\n");
3728 goto err;
3731 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3732 if (FAILED( res ))
3734 ERR("QueryInterface(IID_IPersistFile) failed\n");
3735 goto err;
3738 target = MSI_RecordGetString(row, 5);
3739 if (strchrW(target, '['))
3741 deformat_string( package, target, &path );
3742 TRACE("target path is %s\n", debugstr_w(path));
3743 IShellLinkW_SetPath( sl, path );
3744 msi_free( path );
3746 else
3748 FIXME("poorly handled shortcut format, advertised shortcut\n");
3749 IShellLinkW_SetPath(sl,comp->FullKeypath);
3752 if (!MSI_RecordIsNull(row,6))
3754 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3755 deformat_string(package, arguments, &deformated);
3756 IShellLinkW_SetArguments(sl,deformated);
3757 msi_free(deformated);
3760 if (!MSI_RecordIsNull(row,7))
3762 LPCWSTR description = MSI_RecordGetString(row, 7);
3763 IShellLinkW_SetDescription(sl, description);
3766 if (!MSI_RecordIsNull(row,8))
3767 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3769 if (!MSI_RecordIsNull(row,9))
3771 INT index;
3772 LPCWSTR icon = MSI_RecordGetString(row, 9);
3774 path = msi_build_icon_path(package, icon);
3775 index = MSI_RecordGetInteger(row,10);
3777 /* no value means 0 */
3778 if (index == MSI_NULL_INTEGER)
3779 index = 0;
3781 IShellLinkW_SetIconLocation(sl, path, index);
3782 msi_free(path);
3785 if (!MSI_RecordIsNull(row,11))
3786 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3788 if (!MSI_RecordIsNull(row,12))
3790 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3791 full_path = msi_get_target_folder( package, wkdir );
3792 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3794 link_file = get_link_file(package, row);
3796 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3797 IPersistFile_Save(pf, link_file, FALSE);
3798 msi_free(link_file);
3800 err:
3801 if (pf)
3802 IPersistFile_Release( pf );
3803 if (sl)
3804 IShellLinkW_Release( sl );
3806 return ERROR_SUCCESS;
3809 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3811 static const WCHAR query[] = {
3812 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3813 '`','S','h','o','r','t','c','u','t','`',0};
3814 MSIQUERY *view;
3815 HRESULT res;
3816 UINT rc;
3818 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3819 if (rc != ERROR_SUCCESS)
3820 return ERROR_SUCCESS;
3822 res = CoInitialize( NULL );
3824 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3825 msiobj_release(&view->hdr);
3827 if (SUCCEEDED(res)) CoUninitialize();
3828 return rc;
3831 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3833 MSIPACKAGE *package = param;
3834 LPWSTR link_file;
3835 LPCWSTR component;
3836 MSICOMPONENT *comp;
3838 component = MSI_RecordGetString( row, 4 );
3839 comp = msi_get_loaded_component( package, component );
3840 if (!comp)
3841 return ERROR_SUCCESS;
3843 comp->Action = msi_get_component_action( package, comp );
3844 if (comp->Action != INSTALLSTATE_ABSENT)
3846 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3847 return ERROR_SUCCESS;
3849 msi_ui_actiondata( package, szRemoveShortcuts, row );
3851 link_file = get_link_file( package, row );
3853 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3854 if (!DeleteFileW( link_file ))
3856 WARN("Failed to remove shortcut file %u\n", GetLastError());
3858 msi_free( link_file );
3860 return ERROR_SUCCESS;
3863 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3865 static const WCHAR query[] = {
3866 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3867 '`','S','h','o','r','t','c','u','t','`',0};
3868 MSIQUERY *view;
3869 UINT rc;
3871 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3872 if (rc != ERROR_SUCCESS)
3873 return ERROR_SUCCESS;
3875 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3876 msiobj_release( &view->hdr );
3877 return rc;
3880 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3882 MSIPACKAGE* package = param;
3883 HANDLE the_file;
3884 LPWSTR FilePath;
3885 LPCWSTR FileName;
3886 CHAR buffer[1024];
3887 DWORD sz;
3888 UINT rc;
3890 FileName = MSI_RecordGetString(row,1);
3891 if (!FileName)
3893 ERR("Unable to get FileName\n");
3894 return ERROR_SUCCESS;
3897 FilePath = msi_build_icon_path(package, FileName);
3899 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3901 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3902 FILE_ATTRIBUTE_NORMAL, NULL);
3904 if (the_file == INVALID_HANDLE_VALUE)
3906 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3907 msi_free(FilePath);
3908 return ERROR_SUCCESS;
3913 DWORD write;
3914 sz = 1024;
3915 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3916 if (rc != ERROR_SUCCESS)
3918 ERR("Failed to get stream\n");
3919 CloseHandle(the_file);
3920 DeleteFileW(FilePath);
3921 break;
3923 WriteFile(the_file,buffer,sz,&write,NULL);
3924 } while (sz == 1024);
3926 msi_free(FilePath);
3927 CloseHandle(the_file);
3929 return ERROR_SUCCESS;
3932 static UINT msi_publish_icons(MSIPACKAGE *package)
3934 static const WCHAR query[]= {
3935 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3936 '`','I','c','o','n','`',0};
3937 MSIQUERY *view;
3938 UINT r;
3940 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3941 if (r == ERROR_SUCCESS)
3943 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3944 msiobj_release(&view->hdr);
3945 if (r != ERROR_SUCCESS)
3946 return r;
3948 return ERROR_SUCCESS;
3951 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3953 UINT r;
3954 HKEY source;
3955 LPWSTR buffer;
3956 MSIMEDIADISK *disk;
3957 MSISOURCELISTINFO *info;
3959 r = RegCreateKeyW(hkey, szSourceList, &source);
3960 if (r != ERROR_SUCCESS)
3961 return r;
3963 RegCloseKey(source);
3965 buffer = strrchrW(package->PackagePath, '\\') + 1;
3966 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3967 package->Context, MSICODE_PRODUCT,
3968 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3969 if (r != ERROR_SUCCESS)
3970 return r;
3972 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3973 package->Context, MSICODE_PRODUCT,
3974 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3975 if (r != ERROR_SUCCESS)
3976 return r;
3978 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3979 package->Context, MSICODE_PRODUCT,
3980 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3981 if (r != ERROR_SUCCESS)
3982 return r;
3984 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3986 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3987 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3988 info->options, info->value);
3989 else
3990 MsiSourceListSetInfoW(package->ProductCode, NULL,
3991 info->context, info->options,
3992 info->property, info->value);
3995 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3997 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3998 disk->context, disk->options,
3999 disk->disk_id, disk->volume_label, disk->disk_prompt);
4002 return ERROR_SUCCESS;
4005 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4007 MSIHANDLE hdb, suminfo;
4008 WCHAR guids[MAX_PATH];
4009 WCHAR packcode[SQUISH_GUID_SIZE];
4010 LPWSTR buffer;
4011 LPWSTR ptr;
4012 DWORD langid;
4013 DWORD size;
4014 UINT r;
4016 static const WCHAR szARPProductIcon[] =
4017 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4018 static const WCHAR szAssignment[] =
4019 {'A','s','s','i','g','n','m','e','n','t',0};
4020 static const WCHAR szAdvertiseFlags[] =
4021 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4022 static const WCHAR szClients[] =
4023 {'C','l','i','e','n','t','s',0};
4024 static const WCHAR szColon[] = {':',0};
4026 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4027 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4028 msi_free(buffer);
4030 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4031 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4033 /* FIXME */
4034 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4036 buffer = msi_dup_property(package->db, szARPProductIcon);
4037 if (buffer)
4039 LPWSTR path = msi_build_icon_path(package, buffer);
4040 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4041 msi_free(path);
4042 msi_free(buffer);
4045 buffer = msi_dup_property(package->db, szProductVersion);
4046 if (buffer)
4048 DWORD verdword = msi_version_str_to_dword(buffer);
4049 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4050 msi_free(buffer);
4053 msi_reg_set_val_dword(hkey, szAssignment, 0);
4054 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4055 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4056 msi_reg_set_val_str(hkey, szClients, szColon);
4058 hdb = alloc_msihandle(&package->db->hdr);
4059 if (!hdb)
4060 return ERROR_NOT_ENOUGH_MEMORY;
4062 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4063 MsiCloseHandle(hdb);
4064 if (r != ERROR_SUCCESS)
4065 goto done;
4067 size = MAX_PATH;
4068 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4069 NULL, guids, &size);
4070 if (r != ERROR_SUCCESS)
4071 goto done;
4073 ptr = strchrW(guids, ';');
4074 if (ptr) *ptr = 0;
4075 squash_guid(guids, packcode);
4076 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4078 done:
4079 MsiCloseHandle(suminfo);
4080 return ERROR_SUCCESS;
4083 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4085 UINT r;
4086 HKEY hkey;
4087 LPWSTR upgrade;
4088 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4090 upgrade = msi_dup_property(package->db, szUpgradeCode);
4091 if (!upgrade)
4092 return ERROR_SUCCESS;
4094 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4095 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4096 else
4097 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4099 if (r != ERROR_SUCCESS)
4101 WARN("failed to open upgrade code key\n");
4102 msi_free(upgrade);
4103 return ERROR_SUCCESS;
4105 squash_guid(package->ProductCode, squashed_pc);
4106 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4107 RegCloseKey(hkey);
4108 msi_free(upgrade);
4109 return ERROR_SUCCESS;
4112 static BOOL msi_check_publish(MSIPACKAGE *package)
4114 MSIFEATURE *feature;
4116 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4118 feature->Action = msi_get_feature_action( package, feature );
4119 if (feature->Action == INSTALLSTATE_LOCAL)
4120 return TRUE;
4123 return FALSE;
4126 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4128 MSIFEATURE *feature;
4130 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4132 feature->Action = msi_get_feature_action( package, feature );
4133 if (feature->Action != INSTALLSTATE_ABSENT)
4134 return FALSE;
4137 return TRUE;
4140 static UINT msi_publish_patches( MSIPACKAGE *package )
4142 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4143 WCHAR patch_squashed[GUID_SIZE];
4144 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4145 LONG res;
4146 MSIPATCHINFO *patch;
4147 UINT r;
4148 WCHAR *p, *all_patches = NULL;
4149 DWORD len = 0;
4151 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4152 if (r != ERROR_SUCCESS)
4153 return ERROR_FUNCTION_FAILED;
4155 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4156 if (res != ERROR_SUCCESS)
4158 r = ERROR_FUNCTION_FAILED;
4159 goto done;
4162 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4163 if (r != ERROR_SUCCESS)
4164 goto done;
4166 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4168 squash_guid( patch->patchcode, patch_squashed );
4169 len += strlenW( patch_squashed ) + 1;
4172 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4173 if (!all_patches)
4174 goto done;
4176 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4178 HKEY patch_key;
4180 squash_guid( patch->patchcode, p );
4181 p += strlenW( p ) + 1;
4183 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4184 (const BYTE *)patch->transforms,
4185 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4186 if (res != ERROR_SUCCESS)
4187 goto done;
4189 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4190 if (r != ERROR_SUCCESS)
4191 goto done;
4193 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4194 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4195 RegCloseKey( patch_key );
4196 if (res != ERROR_SUCCESS)
4197 goto done;
4199 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4201 res = GetLastError();
4202 ERR("Unable to copy patch package %d\n", res);
4203 goto done;
4205 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4206 if (res != ERROR_SUCCESS)
4207 goto done;
4209 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4210 RegCloseKey( patch_key );
4211 if (res != ERROR_SUCCESS)
4212 goto done;
4215 all_patches[len] = 0;
4216 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4217 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4218 if (res != ERROR_SUCCESS)
4219 goto done;
4221 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4222 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4223 if (res != ERROR_SUCCESS)
4224 r = ERROR_FUNCTION_FAILED;
4226 done:
4227 RegCloseKey( product_patches_key );
4228 RegCloseKey( patches_key );
4229 RegCloseKey( product_key );
4230 msi_free( all_patches );
4231 return r;
4234 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4236 UINT rc;
4237 HKEY hukey = NULL, hudkey = NULL;
4238 MSIRECORD *uirow;
4240 if (!list_empty(&package->patches))
4242 rc = msi_publish_patches(package);
4243 if (rc != ERROR_SUCCESS)
4244 goto end;
4247 /* FIXME: also need to publish if the product is in advertise mode */
4248 if (!msi_check_publish(package))
4249 return ERROR_SUCCESS;
4251 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4252 &hukey, TRUE);
4253 if (rc != ERROR_SUCCESS)
4254 goto end;
4256 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4257 NULL, &hudkey, TRUE);
4258 if (rc != ERROR_SUCCESS)
4259 goto end;
4261 rc = msi_publish_upgrade_code(package);
4262 if (rc != ERROR_SUCCESS)
4263 goto end;
4265 rc = msi_publish_product_properties(package, hukey);
4266 if (rc != ERROR_SUCCESS)
4267 goto end;
4269 rc = msi_publish_sourcelist(package, hukey);
4270 if (rc != ERROR_SUCCESS)
4271 goto end;
4273 rc = msi_publish_icons(package);
4275 end:
4276 uirow = MSI_CreateRecord( 1 );
4277 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4278 msi_ui_actiondata( package, szPublishProduct, uirow );
4279 msiobj_release( &uirow->hdr );
4281 RegCloseKey(hukey);
4282 RegCloseKey(hudkey);
4283 return rc;
4286 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4288 WCHAR *filename, *ptr, *folder, *ret;
4289 const WCHAR *dirprop;
4291 filename = msi_dup_record_field( row, 2 );
4292 if (filename && (ptr = strchrW( filename, '|' )))
4293 ptr++;
4294 else
4295 ptr = filename;
4297 dirprop = MSI_RecordGetString( row, 3 );
4298 if (dirprop)
4300 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4301 if (!folder) folder = msi_dup_property( package->db, dirprop );
4303 else
4304 folder = msi_dup_property( package->db, szWindowsFolder );
4306 if (!folder)
4308 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4309 msi_free( filename );
4310 return NULL;
4313 ret = msi_build_directory_name( 2, folder, ptr );
4315 msi_free( filename );
4316 msi_free( folder );
4317 return ret;
4320 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4322 MSIPACKAGE *package = param;
4323 LPCWSTR component, section, key, value, identifier;
4324 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4325 MSIRECORD * uirow;
4326 INT action;
4327 MSICOMPONENT *comp;
4329 component = MSI_RecordGetString(row, 8);
4330 comp = msi_get_loaded_component(package,component);
4331 if (!comp)
4332 return ERROR_SUCCESS;
4334 comp->Action = msi_get_component_action( package, comp );
4335 if (comp->Action != INSTALLSTATE_LOCAL)
4337 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4338 return ERROR_SUCCESS;
4341 identifier = MSI_RecordGetString(row,1);
4342 section = MSI_RecordGetString(row,4);
4343 key = MSI_RecordGetString(row,5);
4344 value = MSI_RecordGetString(row,6);
4345 action = MSI_RecordGetInteger(row,7);
4347 deformat_string(package,section,&deformated_section);
4348 deformat_string(package,key,&deformated_key);
4349 deformat_string(package,value,&deformated_value);
4351 fullname = get_ini_file_name(package, row);
4353 if (action == 0)
4355 TRACE("Adding value %s to section %s in %s\n",
4356 debugstr_w(deformated_key), debugstr_w(deformated_section),
4357 debugstr_w(fullname));
4358 WritePrivateProfileStringW(deformated_section, deformated_key,
4359 deformated_value, fullname);
4361 else if (action == 1)
4363 WCHAR returned[10];
4364 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4365 returned, 10, fullname);
4366 if (returned[0] == 0)
4368 TRACE("Adding value %s to section %s in %s\n",
4369 debugstr_w(deformated_key), debugstr_w(deformated_section),
4370 debugstr_w(fullname));
4372 WritePrivateProfileStringW(deformated_section, deformated_key,
4373 deformated_value, fullname);
4376 else if (action == 3)
4377 FIXME("Append to existing section not yet implemented\n");
4379 uirow = MSI_CreateRecord(4);
4380 MSI_RecordSetStringW(uirow,1,identifier);
4381 MSI_RecordSetStringW(uirow,2,deformated_section);
4382 MSI_RecordSetStringW(uirow,3,deformated_key);
4383 MSI_RecordSetStringW(uirow,4,deformated_value);
4384 msi_ui_actiondata( package, szWriteIniValues, uirow );
4385 msiobj_release( &uirow->hdr );
4387 msi_free(fullname);
4388 msi_free(deformated_key);
4389 msi_free(deformated_value);
4390 msi_free(deformated_section);
4391 return ERROR_SUCCESS;
4394 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4396 static const WCHAR query[] = {
4397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4398 '`','I','n','i','F','i','l','e','`',0};
4399 MSIQUERY *view;
4400 UINT rc;
4402 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4403 if (rc != ERROR_SUCCESS)
4404 return ERROR_SUCCESS;
4406 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4407 msiobj_release(&view->hdr);
4408 return rc;
4411 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4413 MSIPACKAGE *package = param;
4414 LPCWSTR component, section, key, value, identifier;
4415 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4416 MSICOMPONENT *comp;
4417 MSIRECORD *uirow;
4418 INT action;
4420 component = MSI_RecordGetString( row, 8 );
4421 comp = msi_get_loaded_component( package, component );
4422 if (!comp)
4423 return ERROR_SUCCESS;
4425 comp->Action = msi_get_component_action( package, comp );
4426 if (comp->Action != INSTALLSTATE_ABSENT)
4428 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4429 return ERROR_SUCCESS;
4432 identifier = MSI_RecordGetString( row, 1 );
4433 section = MSI_RecordGetString( row, 4 );
4434 key = MSI_RecordGetString( row, 5 );
4435 value = MSI_RecordGetString( row, 6 );
4436 action = MSI_RecordGetInteger( row, 7 );
4438 deformat_string( package, section, &deformated_section );
4439 deformat_string( package, key, &deformated_key );
4440 deformat_string( package, value, &deformated_value );
4442 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4444 filename = get_ini_file_name( package, row );
4446 TRACE("Removing key %s from section %s in %s\n",
4447 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4449 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4451 WARN("Unable to remove key %u\n", GetLastError());
4453 msi_free( filename );
4455 else
4456 FIXME("Unsupported action %d\n", action);
4459 uirow = MSI_CreateRecord( 4 );
4460 MSI_RecordSetStringW( uirow, 1, identifier );
4461 MSI_RecordSetStringW( uirow, 2, deformated_section );
4462 MSI_RecordSetStringW( uirow, 3, deformated_key );
4463 MSI_RecordSetStringW( uirow, 4, deformated_value );
4464 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4465 msiobj_release( &uirow->hdr );
4467 msi_free( deformated_key );
4468 msi_free( deformated_value );
4469 msi_free( deformated_section );
4470 return ERROR_SUCCESS;
4473 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4475 MSIPACKAGE *package = param;
4476 LPCWSTR component, section, key, value, identifier;
4477 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4478 MSICOMPONENT *comp;
4479 MSIRECORD *uirow;
4480 INT action;
4482 component = MSI_RecordGetString( row, 8 );
4483 comp = msi_get_loaded_component( package, component );
4484 if (!comp)
4485 return ERROR_SUCCESS;
4487 comp->Action = msi_get_component_action( package, comp );
4488 if (comp->Action != INSTALLSTATE_LOCAL)
4490 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4491 return ERROR_SUCCESS;
4494 identifier = MSI_RecordGetString( row, 1 );
4495 section = MSI_RecordGetString( row, 4 );
4496 key = MSI_RecordGetString( row, 5 );
4497 value = MSI_RecordGetString( row, 6 );
4498 action = MSI_RecordGetInteger( row, 7 );
4500 deformat_string( package, section, &deformated_section );
4501 deformat_string( package, key, &deformated_key );
4502 deformat_string( package, value, &deformated_value );
4504 if (action == msidbIniFileActionRemoveLine)
4506 filename = get_ini_file_name( package, row );
4508 TRACE("Removing key %s from section %s in %s\n",
4509 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4511 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4513 WARN("Unable to remove key %u\n", GetLastError());
4515 msi_free( filename );
4517 else
4518 FIXME("Unsupported action %d\n", action);
4520 uirow = MSI_CreateRecord( 4 );
4521 MSI_RecordSetStringW( uirow, 1, identifier );
4522 MSI_RecordSetStringW( uirow, 2, deformated_section );
4523 MSI_RecordSetStringW( uirow, 3, deformated_key );
4524 MSI_RecordSetStringW( uirow, 4, deformated_value );
4525 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4526 msiobj_release( &uirow->hdr );
4528 msi_free( deformated_key );
4529 msi_free( deformated_value );
4530 msi_free( deformated_section );
4531 return ERROR_SUCCESS;
4534 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4536 static const WCHAR query[] = {
4537 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4538 '`','I','n','i','F','i','l','e','`',0};
4539 static const WCHAR remove_query[] = {
4540 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4541 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4542 MSIQUERY *view;
4543 UINT rc;
4545 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4546 if (rc == ERROR_SUCCESS)
4548 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4549 msiobj_release( &view->hdr );
4550 if (rc != ERROR_SUCCESS)
4551 return rc;
4553 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4554 if (rc == ERROR_SUCCESS)
4556 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4557 msiobj_release( &view->hdr );
4558 if (rc != ERROR_SUCCESS)
4559 return rc;
4561 return ERROR_SUCCESS;
4564 static void register_dll( const WCHAR *dll, BOOL unregister )
4566 HMODULE hmod;
4568 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4569 if (hmod)
4571 HRESULT (WINAPI *func_ptr)( void );
4572 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4574 func_ptr = (void *)GetProcAddress( hmod, func );
4575 if (func_ptr)
4577 HRESULT hr = func_ptr();
4578 if (FAILED( hr ))
4579 WARN("failed to register dll 0x%08x\n", hr);
4581 else
4582 WARN("entry point %s not found\n", func);
4583 FreeLibrary( hmod );
4584 return;
4586 WARN("failed to load library %u\n", GetLastError());
4589 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4591 MSIPACKAGE *package = param;
4592 LPCWSTR filename;
4593 MSIFILE *file;
4594 MSIRECORD *uirow;
4596 filename = MSI_RecordGetString( row, 1 );
4597 file = msi_get_loaded_file( package, filename );
4598 if (!file)
4600 WARN("unable to find file %s\n", debugstr_w(filename));
4601 return ERROR_SUCCESS;
4603 file->Component->Action = msi_get_component_action( package, file->Component );
4604 if (file->Component->Action != INSTALLSTATE_LOCAL)
4606 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4607 return ERROR_SUCCESS;
4610 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4611 register_dll( file->TargetPath, FALSE );
4613 uirow = MSI_CreateRecord( 2 );
4614 MSI_RecordSetStringW( uirow, 1, file->File );
4615 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4616 msi_ui_actiondata( package, szSelfRegModules, uirow );
4617 msiobj_release( &uirow->hdr );
4619 return ERROR_SUCCESS;
4622 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4624 static const WCHAR query[] = {
4625 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4626 '`','S','e','l','f','R','e','g','`',0};
4627 MSIQUERY *view;
4628 UINT rc;
4630 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4631 if (rc != ERROR_SUCCESS)
4632 return ERROR_SUCCESS;
4634 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4635 msiobj_release(&view->hdr);
4636 return rc;
4639 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4641 MSIPACKAGE *package = param;
4642 LPCWSTR filename;
4643 MSIFILE *file;
4644 MSIRECORD *uirow;
4646 filename = MSI_RecordGetString( row, 1 );
4647 file = msi_get_loaded_file( package, filename );
4648 if (!file)
4650 WARN("unable to find file %s\n", debugstr_w(filename));
4651 return ERROR_SUCCESS;
4653 file->Component->Action = msi_get_component_action( package, file->Component );
4654 if (file->Component->Action != INSTALLSTATE_ABSENT)
4656 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4657 return ERROR_SUCCESS;
4660 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4661 register_dll( file->TargetPath, TRUE );
4663 uirow = MSI_CreateRecord( 2 );
4664 MSI_RecordSetStringW( uirow, 1, file->File );
4665 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4666 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4667 msiobj_release( &uirow->hdr );
4669 return ERROR_SUCCESS;
4672 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4674 static const WCHAR query[] = {
4675 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4676 '`','S','e','l','f','R','e','g','`',0};
4677 MSIQUERY *view;
4678 UINT rc;
4680 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4681 if (rc != ERROR_SUCCESS)
4682 return ERROR_SUCCESS;
4684 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4685 msiobj_release( &view->hdr );
4686 return rc;
4689 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4691 MSIFEATURE *feature;
4692 UINT rc;
4693 HKEY hkey = NULL, userdata = NULL;
4695 if (!msi_check_publish(package))
4696 return ERROR_SUCCESS;
4698 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4699 &hkey, TRUE);
4700 if (rc != ERROR_SUCCESS)
4701 goto end;
4703 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4704 &userdata, TRUE);
4705 if (rc != ERROR_SUCCESS)
4706 goto end;
4708 /* here the guids are base 85 encoded */
4709 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4711 ComponentList *cl;
4712 LPWSTR data = NULL;
4713 GUID clsid;
4714 INT size;
4715 BOOL absent = FALSE;
4716 MSIRECORD *uirow;
4718 if (feature->Action != INSTALLSTATE_LOCAL &&
4719 feature->Action != INSTALLSTATE_SOURCE &&
4720 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4722 size = 1;
4723 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4725 size += 21;
4727 if (feature->Feature_Parent)
4728 size += strlenW( feature->Feature_Parent )+2;
4730 data = msi_alloc(size * sizeof(WCHAR));
4732 data[0] = 0;
4733 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4735 MSICOMPONENT* component = cl->component;
4736 WCHAR buf[21];
4738 buf[0] = 0;
4739 if (component->ComponentId)
4741 TRACE("From %s\n",debugstr_w(component->ComponentId));
4742 CLSIDFromString(component->ComponentId, &clsid);
4743 encode_base85_guid(&clsid,buf);
4744 TRACE("to %s\n",debugstr_w(buf));
4745 strcatW(data,buf);
4749 if (feature->Feature_Parent)
4751 static const WCHAR sep[] = {'\2',0};
4752 strcatW(data,sep);
4753 strcatW(data,feature->Feature_Parent);
4756 msi_reg_set_val_str( userdata, feature->Feature, data );
4757 msi_free(data);
4759 size = 0;
4760 if (feature->Feature_Parent)
4761 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4762 if (!absent)
4764 size += sizeof(WCHAR);
4765 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4766 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4768 else
4770 size += 2*sizeof(WCHAR);
4771 data = msi_alloc(size);
4772 data[0] = 0x6;
4773 data[1] = 0;
4774 if (feature->Feature_Parent)
4775 strcpyW( &data[1], feature->Feature_Parent );
4776 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4777 (LPBYTE)data,size);
4778 msi_free(data);
4781 /* the UI chunk */
4782 uirow = MSI_CreateRecord( 1 );
4783 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4784 msi_ui_actiondata( package, szPublishFeatures, uirow );
4785 msiobj_release( &uirow->hdr );
4786 /* FIXME: call msi_ui_progress? */
4789 end:
4790 RegCloseKey(hkey);
4791 RegCloseKey(userdata);
4792 return rc;
4795 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4797 UINT r;
4798 HKEY hkey;
4799 MSIRECORD *uirow;
4801 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4803 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4804 &hkey, FALSE);
4805 if (r == ERROR_SUCCESS)
4807 RegDeleteValueW(hkey, feature->Feature);
4808 RegCloseKey(hkey);
4811 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4812 &hkey, FALSE);
4813 if (r == ERROR_SUCCESS)
4815 RegDeleteValueW(hkey, feature->Feature);
4816 RegCloseKey(hkey);
4819 uirow = MSI_CreateRecord( 1 );
4820 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4821 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4822 msiobj_release( &uirow->hdr );
4824 return ERROR_SUCCESS;
4827 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4829 MSIFEATURE *feature;
4831 if (!msi_check_unpublish(package))
4832 return ERROR_SUCCESS;
4834 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4836 msi_unpublish_feature(package, feature);
4839 return ERROR_SUCCESS;
4842 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4844 SYSTEMTIME systime;
4845 DWORD size, langid;
4846 WCHAR date[9], *val, *buffer;
4847 const WCHAR *prop, *key;
4849 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4850 static const WCHAR modpath_fmt[] =
4851 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4852 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4853 static const WCHAR szModifyPath[] =
4854 {'M','o','d','i','f','y','P','a','t','h',0};
4855 static const WCHAR szUninstallString[] =
4856 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4857 static const WCHAR szEstimatedSize[] =
4858 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4859 static const WCHAR szDisplayVersion[] =
4860 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4861 static const WCHAR szInstallSource[] =
4862 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4863 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4864 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4865 static const WCHAR szAuthorizedCDFPrefix[] =
4866 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4867 static const WCHAR szARPCONTACT[] =
4868 {'A','R','P','C','O','N','T','A','C','T',0};
4869 static const WCHAR szContact[] =
4870 {'C','o','n','t','a','c','t',0};
4871 static const WCHAR szARPCOMMENTS[] =
4872 {'A','R','P','C','O','M','M','E','N','T','S',0};
4873 static const WCHAR szComments[] =
4874 {'C','o','m','m','e','n','t','s',0};
4875 static const WCHAR szProductName[] =
4876 {'P','r','o','d','u','c','t','N','a','m','e',0};
4877 static const WCHAR szDisplayName[] =
4878 {'D','i','s','p','l','a','y','N','a','m','e',0};
4879 static const WCHAR szARPHELPLINK[] =
4880 {'A','R','P','H','E','L','P','L','I','N','K',0};
4881 static const WCHAR szHelpLink[] =
4882 {'H','e','l','p','L','i','n','k',0};
4883 static const WCHAR szARPHELPTELEPHONE[] =
4884 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4885 static const WCHAR szHelpTelephone[] =
4886 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4887 static const WCHAR szARPINSTALLLOCATION[] =
4888 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4889 static const WCHAR szManufacturer[] =
4890 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4891 static const WCHAR szPublisher[] =
4892 {'P','u','b','l','i','s','h','e','r',0};
4893 static const WCHAR szARPREADME[] =
4894 {'A','R','P','R','E','A','D','M','E',0};
4895 static const WCHAR szReadme[] =
4896 {'R','e','a','d','M','e',0};
4897 static const WCHAR szARPSIZE[] =
4898 {'A','R','P','S','I','Z','E',0};
4899 static const WCHAR szSize[] =
4900 {'S','i','z','e',0};
4901 static const WCHAR szARPURLINFOABOUT[] =
4902 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4903 static const WCHAR szURLInfoAbout[] =
4904 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4905 static const WCHAR szARPURLUPDATEINFO[] =
4906 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4907 static const WCHAR szURLUpdateInfo[] =
4908 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4909 static const WCHAR szARPSYSTEMCOMPONENT[] =
4910 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4911 static const WCHAR szSystemComponent[] =
4912 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4914 static const WCHAR *propval[] = {
4915 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4916 szARPCONTACT, szContact,
4917 szARPCOMMENTS, szComments,
4918 szProductName, szDisplayName,
4919 szARPHELPLINK, szHelpLink,
4920 szARPHELPTELEPHONE, szHelpTelephone,
4921 szARPINSTALLLOCATION, szInstallLocation,
4922 szSourceDir, szInstallSource,
4923 szManufacturer, szPublisher,
4924 szARPREADME, szReadme,
4925 szARPSIZE, szSize,
4926 szARPURLINFOABOUT, szURLInfoAbout,
4927 szARPURLUPDATEINFO, szURLUpdateInfo,
4928 NULL
4930 const WCHAR **p = propval;
4932 while (*p)
4934 prop = *p++;
4935 key = *p++;
4936 val = msi_dup_property(package->db, prop);
4937 msi_reg_set_val_str(hkey, key, val);
4938 msi_free(val);
4941 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4942 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4944 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4946 size = deformat_string(package, modpath_fmt, &buffer);
4947 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4948 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4949 msi_free(buffer);
4951 /* FIXME: Write real Estimated Size when we have it */
4952 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4954 GetLocalTime(&systime);
4955 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4956 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4958 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4959 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4961 buffer = msi_dup_property(package->db, szProductVersion);
4962 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4963 if (buffer)
4965 DWORD verdword = msi_version_str_to_dword(buffer);
4967 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4968 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4969 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4970 msi_free(buffer);
4973 return ERROR_SUCCESS;
4976 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4978 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4979 MSIRECORD *uirow;
4980 LPWSTR upgrade_code;
4981 HKEY hkey, props, upgrade_key;
4982 UINT rc;
4984 /* FIXME: also need to publish if the product is in advertise mode */
4985 if (!msi_check_publish(package))
4986 return ERROR_SUCCESS;
4988 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4989 if (rc != ERROR_SUCCESS)
4990 return rc;
4992 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4993 if (rc != ERROR_SUCCESS)
4994 goto done;
4996 rc = msi_publish_install_properties(package, hkey);
4997 if (rc != ERROR_SUCCESS)
4998 goto done;
5000 rc = msi_publish_install_properties(package, props);
5001 if (rc != ERROR_SUCCESS)
5002 goto done;
5004 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5005 if (upgrade_code)
5007 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5008 if (rc == ERROR_SUCCESS)
5010 squash_guid( package->ProductCode, squashed_pc );
5011 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5012 RegCloseKey( upgrade_key );
5014 msi_free( upgrade_code );
5016 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5017 package->delete_on_close = FALSE;
5019 done:
5020 uirow = MSI_CreateRecord( 1 );
5021 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5022 msi_ui_actiondata( package, szRegisterProduct, uirow );
5023 msiobj_release( &uirow->hdr );
5025 RegCloseKey(hkey);
5026 return ERROR_SUCCESS;
5029 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5031 return execute_script(package, SCRIPT_INSTALL);
5034 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5036 MSIPACKAGE *package = param;
5037 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5038 WCHAR *p, *icon_path;
5040 if (!icon) return ERROR_SUCCESS;
5041 if ((icon_path = msi_build_icon_path( package, icon )))
5043 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5044 DeleteFileW( icon_path );
5045 if ((p = strrchrW( icon_path, '\\' )))
5047 *p = 0;
5048 RemoveDirectoryW( icon_path );
5050 msi_free( icon_path );
5052 return ERROR_SUCCESS;
5055 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5057 static const WCHAR query[]= {
5058 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5059 MSIQUERY *view;
5060 UINT r;
5062 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5063 if (r == ERROR_SUCCESS)
5065 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5066 msiobj_release( &view->hdr );
5067 if (r != ERROR_SUCCESS)
5068 return r;
5070 return ERROR_SUCCESS;
5073 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5075 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5076 WCHAR *upgrade, **features;
5077 BOOL full_uninstall = TRUE;
5078 MSIFEATURE *feature;
5079 MSIPATCHINFO *patch;
5080 UINT i;
5082 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5084 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5086 features = msi_split_string( remove, ',' );
5087 for (i = 0; features && features[i]; i++)
5089 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5091 msi_free(features);
5093 if (!full_uninstall)
5094 return ERROR_SUCCESS;
5096 MSIREG_DeleteProductKey(package->ProductCode);
5097 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5098 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5100 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5101 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5102 MSIREG_DeleteUserProductKey(package->ProductCode);
5103 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5105 upgrade = msi_dup_property(package->db, szUpgradeCode);
5106 if (upgrade)
5108 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5109 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5110 msi_free(upgrade);
5113 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5115 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5116 if (!strcmpW( package->ProductCode, patch->products ))
5118 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5119 patch->delete_on_close = TRUE;
5121 /* FIXME: remove local patch package if this is the last product */
5123 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5124 package->delete_on_close = TRUE;
5126 msi_unpublish_icons( package );
5127 return ERROR_SUCCESS;
5130 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5132 UINT rc;
5133 WCHAR *remove;
5135 /* turn off scheduling */
5136 package->script->CurrentlyScripting= FALSE;
5138 /* first do the same as an InstallExecute */
5139 rc = ACTION_InstallExecute(package);
5140 if (rc != ERROR_SUCCESS)
5141 return rc;
5143 /* then handle commit actions */
5144 rc = execute_script(package, SCRIPT_COMMIT);
5145 if (rc != ERROR_SUCCESS)
5146 return rc;
5148 remove = msi_dup_property(package->db, szRemove);
5149 rc = msi_unpublish_product(package, remove);
5150 msi_free(remove);
5151 return rc;
5154 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5156 static const WCHAR RunOnce[] = {
5157 'S','o','f','t','w','a','r','e','\\',
5158 'M','i','c','r','o','s','o','f','t','\\',
5159 'W','i','n','d','o','w','s','\\',
5160 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5161 'R','u','n','O','n','c','e',0};
5162 static const WCHAR InstallRunOnce[] = {
5163 'S','o','f','t','w','a','r','e','\\',
5164 'M','i','c','r','o','s','o','f','t','\\',
5165 'W','i','n','d','o','w','s','\\',
5166 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5167 'I','n','s','t','a','l','l','e','r','\\',
5168 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5170 static const WCHAR msiexec_fmt[] = {
5171 '%','s',
5172 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5173 '\"','%','s','\"',0};
5174 static const WCHAR install_fmt[] = {
5175 '/','I',' ','\"','%','s','\"',' ',
5176 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5177 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5178 WCHAR buffer[256], sysdir[MAX_PATH];
5179 HKEY hkey;
5180 WCHAR squished_pc[100];
5182 squash_guid(package->ProductCode,squished_pc);
5184 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5185 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5186 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5187 squished_pc);
5189 msi_reg_set_val_str( hkey, squished_pc, buffer );
5190 RegCloseKey(hkey);
5192 TRACE("Reboot command %s\n",debugstr_w(buffer));
5194 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5195 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5197 msi_reg_set_val_str( hkey, squished_pc, buffer );
5198 RegCloseKey(hkey);
5200 return ERROR_INSTALL_SUSPEND;
5203 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5205 static const WCHAR query[] =
5206 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5207 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5208 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5209 MSIRECORD *rec, *row;
5210 DWORD i, size = 0;
5211 va_list va;
5212 const WCHAR *str;
5213 WCHAR *data;
5215 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5217 rec = MSI_CreateRecord( count + 2 );
5218 str = MSI_RecordGetString( row, 1 );
5219 MSI_RecordSetStringW( rec, 0, str );
5220 msiobj_release( &row->hdr );
5221 MSI_RecordSetInteger( rec, 1, error );
5223 va_start( va, count );
5224 for (i = 0; i < count; i++)
5226 str = va_arg( va, const WCHAR *);
5227 MSI_RecordSetStringW( rec, i + 2, str );
5229 va_end( va );
5231 MSI_FormatRecordW( package, rec, NULL, &size );
5232 size++;
5233 data = msi_alloc( size * sizeof(WCHAR) );
5234 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5235 else data[0] = 0;
5236 msiobj_release( &rec->hdr );
5237 return data;
5240 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5242 DWORD attrib;
5243 UINT rc;
5246 * We are currently doing what should be done here in the top level Install
5247 * however for Administrative and uninstalls this step will be needed
5249 if (!package->PackagePath)
5250 return ERROR_SUCCESS;
5252 msi_set_sourcedir_props(package, TRUE);
5254 attrib = GetFileAttributesW(package->db->path);
5255 if (attrib == INVALID_FILE_ATTRIBUTES)
5257 LPWSTR prompt, msg;
5258 DWORD size = 0;
5260 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5261 package->Context, MSICODE_PRODUCT,
5262 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5263 if (rc == ERROR_MORE_DATA)
5265 prompt = msi_alloc(size * sizeof(WCHAR));
5266 MsiSourceListGetInfoW(package->ProductCode, NULL,
5267 package->Context, MSICODE_PRODUCT,
5268 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5270 else
5271 prompt = strdupW(package->db->path);
5273 msg = msi_build_error_string(package, 1302, 1, prompt);
5274 msi_free(prompt);
5275 while(attrib == INVALID_FILE_ATTRIBUTES)
5277 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5278 if (rc == IDCANCEL)
5280 msi_free(msg);
5281 return ERROR_INSTALL_USEREXIT;
5283 attrib = GetFileAttributesW(package->db->path);
5285 msi_free(msg);
5286 rc = ERROR_SUCCESS;
5288 else
5289 return ERROR_SUCCESS;
5291 return rc;
5294 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5296 HKEY hkey = 0;
5297 LPWSTR buffer, productid = NULL;
5298 UINT i, rc = ERROR_SUCCESS;
5299 MSIRECORD *uirow;
5301 static const WCHAR szPropKeys[][80] =
5303 {'P','r','o','d','u','c','t','I','D',0},
5304 {'U','S','E','R','N','A','M','E',0},
5305 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5306 {0},
5309 static const WCHAR szRegKeys[][80] =
5311 {'P','r','o','d','u','c','t','I','D',0},
5312 {'R','e','g','O','w','n','e','r',0},
5313 {'R','e','g','C','o','m','p','a','n','y',0},
5314 {0},
5317 if (msi_check_unpublish(package))
5319 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5320 goto end;
5323 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5324 if (!productid)
5325 goto end;
5327 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5328 NULL, &hkey, TRUE);
5329 if (rc != ERROR_SUCCESS)
5330 goto end;
5332 for( i = 0; szPropKeys[i][0]; i++ )
5334 buffer = msi_dup_property( package->db, szPropKeys[i] );
5335 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5336 msi_free( buffer );
5339 end:
5340 uirow = MSI_CreateRecord( 1 );
5341 MSI_RecordSetStringW( uirow, 1, productid );
5342 msi_ui_actiondata( package, szRegisterUser, uirow );
5343 msiobj_release( &uirow->hdr );
5345 msi_free(productid);
5346 RegCloseKey(hkey);
5347 return rc;
5351 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5353 UINT rc;
5355 package->script->InWhatSequence |= SEQUENCE_EXEC;
5356 rc = ACTION_ProcessExecSequence(package,FALSE);
5357 return rc;
5360 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5362 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5363 WCHAR productid_85[21], component_85[21], *ret;
5364 GUID clsid;
5365 DWORD sz;
5367 /* > is used if there is a component GUID and < if not. */
5369 productid_85[0] = 0;
5370 component_85[0] = 0;
5371 CLSIDFromString( package->ProductCode, &clsid );
5373 encode_base85_guid( &clsid, productid_85 );
5374 if (component)
5376 CLSIDFromString( component->ComponentId, &clsid );
5377 encode_base85_guid( &clsid, component_85 );
5380 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5381 debugstr_w(component_85));
5383 sz = 20 + strlenW( feature ) + 20 + 3;
5384 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5385 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5386 return ret;
5389 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5391 MSIPACKAGE *package = param;
5392 LPCWSTR compgroupid, component, feature, qualifier, text;
5393 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5394 HKEY hkey = NULL;
5395 UINT rc;
5396 MSICOMPONENT *comp;
5397 MSIFEATURE *feat;
5398 DWORD sz;
5399 MSIRECORD *uirow;
5400 int len;
5402 feature = MSI_RecordGetString(rec, 5);
5403 feat = msi_get_loaded_feature(package, feature);
5404 if (!feat)
5405 return ERROR_SUCCESS;
5407 feat->Action = msi_get_feature_action( package, feat );
5408 if (feat->Action != INSTALLSTATE_LOCAL &&
5409 feat->Action != INSTALLSTATE_SOURCE &&
5410 feat->Action != INSTALLSTATE_ADVERTISED)
5412 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5413 return ERROR_SUCCESS;
5416 component = MSI_RecordGetString(rec, 3);
5417 comp = msi_get_loaded_component(package, component);
5418 if (!comp)
5419 return ERROR_SUCCESS;
5421 compgroupid = MSI_RecordGetString(rec,1);
5422 qualifier = MSI_RecordGetString(rec,2);
5424 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5425 if (rc != ERROR_SUCCESS)
5426 goto end;
5428 advertise = msi_create_component_advertise_string( package, comp, feature );
5429 text = MSI_RecordGetString( rec, 4 );
5430 if (text)
5432 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5433 strcpyW( p, advertise );
5434 strcatW( p, text );
5435 msi_free( advertise );
5436 advertise = p;
5438 existing = msi_reg_get_val_str( hkey, qualifier );
5440 sz = strlenW( advertise ) + 1;
5441 if (existing)
5443 for (p = existing; *p; p += len)
5445 len = strlenW( p ) + 1;
5446 if (strcmpW( advertise, p )) sz += len;
5449 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5451 rc = ERROR_OUTOFMEMORY;
5452 goto end;
5454 q = output;
5455 if (existing)
5457 for (p = existing; *p; p += len)
5459 len = strlenW( p ) + 1;
5460 if (strcmpW( advertise, p ))
5462 memcpy( q, p, len * sizeof(WCHAR) );
5463 q += len;
5467 strcpyW( q, advertise );
5468 q[strlenW( q ) + 1] = 0;
5470 msi_reg_set_val_multi_str( hkey, qualifier, output );
5472 end:
5473 RegCloseKey(hkey);
5474 msi_free( output );
5475 msi_free( advertise );
5476 msi_free( existing );
5478 /* the UI chunk */
5479 uirow = MSI_CreateRecord( 2 );
5480 MSI_RecordSetStringW( uirow, 1, compgroupid );
5481 MSI_RecordSetStringW( uirow, 2, qualifier);
5482 msi_ui_actiondata( package, szPublishComponents, uirow );
5483 msiobj_release( &uirow->hdr );
5484 /* FIXME: call ui_progress? */
5486 return rc;
5490 * At present I am ignorning the advertised components part of this and only
5491 * focusing on the qualified component sets
5493 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5495 static const WCHAR query[] = {
5496 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5497 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5498 MSIQUERY *view;
5499 UINT rc;
5501 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5502 if (rc != ERROR_SUCCESS)
5503 return ERROR_SUCCESS;
5505 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5506 msiobj_release(&view->hdr);
5507 return rc;
5510 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5512 static const WCHAR szInstallerComponents[] = {
5513 'S','o','f','t','w','a','r','e','\\',
5514 'M','i','c','r','o','s','o','f','t','\\',
5515 'I','n','s','t','a','l','l','e','r','\\',
5516 'C','o','m','p','o','n','e','n','t','s','\\',0};
5518 MSIPACKAGE *package = param;
5519 LPCWSTR compgroupid, component, feature, qualifier;
5520 MSICOMPONENT *comp;
5521 MSIFEATURE *feat;
5522 MSIRECORD *uirow;
5523 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5524 LONG res;
5526 feature = MSI_RecordGetString( rec, 5 );
5527 feat = msi_get_loaded_feature( package, feature );
5528 if (!feat)
5529 return ERROR_SUCCESS;
5531 feat->Action = msi_get_feature_action( package, feat );
5532 if (feat->Action != INSTALLSTATE_ABSENT)
5534 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5535 return ERROR_SUCCESS;
5538 component = MSI_RecordGetString( rec, 3 );
5539 comp = msi_get_loaded_component( package, component );
5540 if (!comp)
5541 return ERROR_SUCCESS;
5543 compgroupid = MSI_RecordGetString( rec, 1 );
5544 qualifier = MSI_RecordGetString( rec, 2 );
5546 squash_guid( compgroupid, squashed );
5547 strcpyW( keypath, szInstallerComponents );
5548 strcatW( keypath, squashed );
5550 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5551 if (res != ERROR_SUCCESS)
5553 WARN("Unable to delete component key %d\n", res);
5556 uirow = MSI_CreateRecord( 2 );
5557 MSI_RecordSetStringW( uirow, 1, compgroupid );
5558 MSI_RecordSetStringW( uirow, 2, qualifier );
5559 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5560 msiobj_release( &uirow->hdr );
5562 return ERROR_SUCCESS;
5565 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5567 static const WCHAR query[] = {
5568 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5569 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5570 MSIQUERY *view;
5571 UINT rc;
5573 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5574 if (rc != ERROR_SUCCESS)
5575 return ERROR_SUCCESS;
5577 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5578 msiobj_release( &view->hdr );
5579 return rc;
5582 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5584 static const WCHAR query[] =
5585 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5586 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5587 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5588 MSIPACKAGE *package = param;
5589 MSICOMPONENT *component;
5590 MSIRECORD *row;
5591 MSIFILE *file;
5592 SC_HANDLE hscm = NULL, service = NULL;
5593 LPCWSTR comp, key;
5594 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5595 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5596 DWORD serv_type, start_type, err_control;
5597 SERVICE_DESCRIPTIONW sd = {NULL};
5599 comp = MSI_RecordGetString( rec, 12 );
5600 component = msi_get_loaded_component( package, comp );
5601 if (!component)
5603 WARN("service component not found\n");
5604 goto done;
5606 component->Action = msi_get_component_action( package, component );
5607 if (component->Action != INSTALLSTATE_LOCAL)
5609 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5610 goto done;
5612 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5613 if (!hscm)
5615 ERR("Failed to open the SC Manager!\n");
5616 goto done;
5619 start_type = MSI_RecordGetInteger(rec, 5);
5620 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5621 goto done;
5623 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5624 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5625 serv_type = MSI_RecordGetInteger(rec, 4);
5626 err_control = MSI_RecordGetInteger(rec, 6);
5627 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5628 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5629 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5630 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5631 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5632 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5634 /* fetch the service path */
5635 row = MSI_QueryGetRecord(package->db, query, comp);
5636 if (!row)
5638 ERR("Query failed\n");
5639 goto done;
5641 key = MSI_RecordGetString(row, 6);
5642 file = msi_get_loaded_file(package, key);
5643 msiobj_release(&row->hdr);
5644 if (!file)
5646 ERR("Failed to load the service file\n");
5647 goto done;
5650 if (!args || !args[0]) image_path = file->TargetPath;
5651 else
5653 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5654 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5655 return ERROR_OUTOFMEMORY;
5657 strcpyW(image_path, file->TargetPath);
5658 strcatW(image_path, szSpace);
5659 strcatW(image_path, args);
5661 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5662 start_type, err_control, image_path, load_order,
5663 NULL, depends, serv_name, pass);
5665 if (!service)
5667 if (GetLastError() != ERROR_SERVICE_EXISTS)
5668 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5670 else if (sd.lpDescription)
5672 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5673 WARN("failed to set service description %u\n", GetLastError());
5676 if (image_path != file->TargetPath) msi_free(image_path);
5677 done:
5678 CloseServiceHandle(service);
5679 CloseServiceHandle(hscm);
5680 msi_free(name);
5681 msi_free(disp);
5682 msi_free(sd.lpDescription);
5683 msi_free(load_order);
5684 msi_free(serv_name);
5685 msi_free(pass);
5686 msi_free(depends);
5687 msi_free(args);
5689 return ERROR_SUCCESS;
5692 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5694 static const WCHAR query[] = {
5695 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5696 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5697 MSIQUERY *view;
5698 UINT rc;
5700 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5701 if (rc != ERROR_SUCCESS)
5702 return ERROR_SUCCESS;
5704 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5705 msiobj_release(&view->hdr);
5706 return rc;
5709 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5710 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5712 LPCWSTR *vector, *temp_vector;
5713 LPWSTR p, q;
5714 DWORD sep_len;
5716 static const WCHAR separator[] = {'[','~',']',0};
5718 *numargs = 0;
5719 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5721 if (!args)
5722 return NULL;
5724 vector = msi_alloc(sizeof(LPWSTR));
5725 if (!vector)
5726 return NULL;
5728 p = args;
5731 (*numargs)++;
5732 vector[*numargs - 1] = p;
5734 if ((q = strstrW(p, separator)))
5736 *q = '\0';
5738 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5739 if (!temp_vector)
5741 msi_free(vector);
5742 return NULL;
5744 vector = temp_vector;
5746 p = q + sep_len;
5748 } while (q);
5750 return vector;
5753 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5755 MSIPACKAGE *package = param;
5756 MSICOMPONENT *comp;
5757 MSIRECORD *uirow;
5758 SC_HANDLE scm = NULL, service = NULL;
5759 LPCWSTR component, *vector = NULL;
5760 LPWSTR name, args, display_name = NULL;
5761 DWORD event, numargs, len, wait, dummy;
5762 UINT r = ERROR_FUNCTION_FAILED;
5763 SERVICE_STATUS_PROCESS status;
5764 ULONGLONG start_time;
5766 component = MSI_RecordGetString(rec, 6);
5767 comp = msi_get_loaded_component(package, component);
5768 if (!comp)
5769 return ERROR_SUCCESS;
5771 comp->Action = msi_get_component_action( package, comp );
5772 if (comp->Action != INSTALLSTATE_LOCAL)
5774 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5775 return ERROR_SUCCESS;
5778 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5779 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5780 event = MSI_RecordGetInteger(rec, 3);
5781 wait = MSI_RecordGetInteger(rec, 5);
5783 if (!(event & msidbServiceControlEventStart))
5785 r = ERROR_SUCCESS;
5786 goto done;
5789 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5790 if (!scm)
5792 ERR("Failed to open the service control manager\n");
5793 goto done;
5796 len = 0;
5797 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5798 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5800 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5801 GetServiceDisplayNameW( scm, name, display_name, &len );
5804 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5805 if (!service)
5807 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5808 goto done;
5811 vector = msi_service_args_to_vector(args, &numargs);
5813 if (!StartServiceW(service, numargs, vector) &&
5814 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5816 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5817 goto done;
5820 r = ERROR_SUCCESS;
5821 if (wait)
5823 /* wait for at most 30 seconds for the service to be up and running */
5824 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5825 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5827 TRACE("failed to query service status (%u)\n", GetLastError());
5828 goto done;
5830 start_time = GetTickCount64();
5831 while (status.dwCurrentState == SERVICE_START_PENDING)
5833 if (GetTickCount64() - start_time > 30000) break;
5834 Sleep(1000);
5835 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5836 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5838 TRACE("failed to query service status (%u)\n", GetLastError());
5839 goto done;
5842 if (status.dwCurrentState != SERVICE_RUNNING)
5844 WARN("service failed to start %u\n", status.dwCurrentState);
5845 r = ERROR_FUNCTION_FAILED;
5849 done:
5850 uirow = MSI_CreateRecord( 2 );
5851 MSI_RecordSetStringW( uirow, 1, display_name );
5852 MSI_RecordSetStringW( uirow, 2, name );
5853 msi_ui_actiondata( package, szStartServices, uirow );
5854 msiobj_release( &uirow->hdr );
5856 CloseServiceHandle(service);
5857 CloseServiceHandle(scm);
5859 msi_free(name);
5860 msi_free(args);
5861 msi_free(vector);
5862 msi_free(display_name);
5863 return r;
5866 static UINT ACTION_StartServices( MSIPACKAGE *package )
5868 static const WCHAR query[] = {
5869 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5870 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5871 MSIQUERY *view;
5872 UINT rc;
5874 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5875 if (rc != ERROR_SUCCESS)
5876 return ERROR_SUCCESS;
5878 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5879 msiobj_release(&view->hdr);
5880 return rc;
5883 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5885 DWORD i, needed, count;
5886 ENUM_SERVICE_STATUSW *dependencies;
5887 SERVICE_STATUS ss;
5888 SC_HANDLE depserv;
5890 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5891 0, &needed, &count))
5892 return TRUE;
5894 if (GetLastError() != ERROR_MORE_DATA)
5895 return FALSE;
5897 dependencies = msi_alloc(needed);
5898 if (!dependencies)
5899 return FALSE;
5901 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5902 needed, &needed, &count))
5903 goto error;
5905 for (i = 0; i < count; i++)
5907 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5908 SERVICE_STOP | SERVICE_QUERY_STATUS);
5909 if (!depserv)
5910 goto error;
5912 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5913 goto error;
5916 return TRUE;
5918 error:
5919 msi_free(dependencies);
5920 return FALSE;
5923 static UINT stop_service( LPCWSTR name )
5925 SC_HANDLE scm = NULL, service = NULL;
5926 SERVICE_STATUS status;
5927 SERVICE_STATUS_PROCESS ssp;
5928 DWORD needed;
5930 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5931 if (!scm)
5933 WARN("Failed to open the SCM: %d\n", GetLastError());
5934 goto done;
5937 service = OpenServiceW(scm, name,
5938 SERVICE_STOP |
5939 SERVICE_QUERY_STATUS |
5940 SERVICE_ENUMERATE_DEPENDENTS);
5941 if (!service)
5943 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5944 goto done;
5947 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5948 sizeof(SERVICE_STATUS_PROCESS), &needed))
5950 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5951 goto done;
5954 if (ssp.dwCurrentState == SERVICE_STOPPED)
5955 goto done;
5957 stop_service_dependents(scm, service);
5959 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5960 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5962 done:
5963 CloseServiceHandle(service);
5964 CloseServiceHandle(scm);
5966 return ERROR_SUCCESS;
5969 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5971 MSIPACKAGE *package = param;
5972 MSICOMPONENT *comp;
5973 MSIRECORD *uirow;
5974 LPCWSTR component;
5975 LPWSTR name = NULL, display_name = NULL;
5976 DWORD event, len;
5977 SC_HANDLE scm;
5979 event = MSI_RecordGetInteger( rec, 3 );
5980 if (!(event & msidbServiceControlEventStop))
5981 return ERROR_SUCCESS;
5983 component = MSI_RecordGetString( rec, 6 );
5984 comp = msi_get_loaded_component( package, component );
5985 if (!comp)
5986 return ERROR_SUCCESS;
5988 comp->Action = msi_get_component_action( package, comp );
5989 if (comp->Action != INSTALLSTATE_ABSENT)
5991 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5992 return ERROR_SUCCESS;
5995 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5996 if (!scm)
5998 ERR("Failed to open the service control manager\n");
5999 goto done;
6002 len = 0;
6003 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6004 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6006 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6007 GetServiceDisplayNameW( scm, name, display_name, &len );
6009 CloseServiceHandle( scm );
6011 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6012 stop_service( name );
6014 done:
6015 uirow = MSI_CreateRecord( 2 );
6016 MSI_RecordSetStringW( uirow, 1, display_name );
6017 MSI_RecordSetStringW( uirow, 2, name );
6018 msi_ui_actiondata( package, szStopServices, uirow );
6019 msiobj_release( &uirow->hdr );
6021 msi_free( name );
6022 msi_free( display_name );
6023 return ERROR_SUCCESS;
6026 static UINT ACTION_StopServices( MSIPACKAGE *package )
6028 static const WCHAR query[] = {
6029 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6030 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6031 MSIQUERY *view;
6032 UINT rc;
6034 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6035 if (rc != ERROR_SUCCESS)
6036 return ERROR_SUCCESS;
6038 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6039 msiobj_release(&view->hdr);
6040 return rc;
6043 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6045 MSIPACKAGE *package = param;
6046 MSICOMPONENT *comp;
6047 MSIRECORD *uirow;
6048 LPWSTR name = NULL, display_name = NULL;
6049 DWORD event, len;
6050 SC_HANDLE scm = NULL, service = NULL;
6052 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6053 if (!comp)
6054 return ERROR_SUCCESS;
6056 event = MSI_RecordGetInteger( rec, 3 );
6057 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6059 comp->Action = msi_get_component_action( package, comp );
6060 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6061 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6063 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6064 msi_free( name );
6065 return ERROR_SUCCESS;
6067 stop_service( name );
6069 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6070 if (!scm)
6072 WARN("Failed to open the SCM: %d\n", GetLastError());
6073 goto done;
6076 len = 0;
6077 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6078 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6080 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6081 GetServiceDisplayNameW( scm, name, display_name, &len );
6084 service = OpenServiceW( scm, name, DELETE );
6085 if (!service)
6087 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6088 goto done;
6091 if (!DeleteService( service ))
6092 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6094 done:
6095 uirow = MSI_CreateRecord( 2 );
6096 MSI_RecordSetStringW( uirow, 1, display_name );
6097 MSI_RecordSetStringW( uirow, 2, name );
6098 msi_ui_actiondata( package, szDeleteServices, uirow );
6099 msiobj_release( &uirow->hdr );
6101 CloseServiceHandle( service );
6102 CloseServiceHandle( scm );
6103 msi_free( name );
6104 msi_free( display_name );
6106 return ERROR_SUCCESS;
6109 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6111 static const WCHAR query[] = {
6112 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6113 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6114 MSIQUERY *view;
6115 UINT rc;
6117 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6118 if (rc != ERROR_SUCCESS)
6119 return ERROR_SUCCESS;
6121 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6122 msiobj_release( &view->hdr );
6123 return rc;
6126 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6128 MSIPACKAGE *package = param;
6129 LPWSTR driver, driver_path, ptr;
6130 WCHAR outpath[MAX_PATH];
6131 MSIFILE *driver_file = NULL, *setup_file = NULL;
6132 MSICOMPONENT *comp;
6133 MSIRECORD *uirow;
6134 LPCWSTR desc, file_key, component;
6135 DWORD len, usage;
6136 UINT r = ERROR_SUCCESS;
6138 static const WCHAR driver_fmt[] = {
6139 'D','r','i','v','e','r','=','%','s',0};
6140 static const WCHAR setup_fmt[] = {
6141 'S','e','t','u','p','=','%','s',0};
6142 static const WCHAR usage_fmt[] = {
6143 'F','i','l','e','U','s','a','g','e','=','1',0};
6145 component = MSI_RecordGetString( rec, 2 );
6146 comp = msi_get_loaded_component( package, component );
6147 if (!comp)
6148 return ERROR_SUCCESS;
6150 comp->Action = msi_get_component_action( package, comp );
6151 if (comp->Action != INSTALLSTATE_LOCAL)
6153 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6154 return ERROR_SUCCESS;
6156 desc = MSI_RecordGetString(rec, 3);
6158 file_key = MSI_RecordGetString( rec, 4 );
6159 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6161 file_key = MSI_RecordGetString( rec, 5 );
6162 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6164 if (!driver_file)
6166 ERR("ODBC Driver entry not found!\n");
6167 return ERROR_FUNCTION_FAILED;
6170 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6171 if (setup_file)
6172 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6173 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6175 driver = msi_alloc(len * sizeof(WCHAR));
6176 if (!driver)
6177 return ERROR_OUTOFMEMORY;
6179 ptr = driver;
6180 lstrcpyW(ptr, desc);
6181 ptr += lstrlenW(ptr) + 1;
6183 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6184 ptr += len + 1;
6186 if (setup_file)
6188 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6189 ptr += len + 1;
6192 lstrcpyW(ptr, usage_fmt);
6193 ptr += lstrlenW(ptr) + 1;
6194 *ptr = '\0';
6196 if (!driver_file->TargetPath)
6198 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6199 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6201 driver_path = strdupW(driver_file->TargetPath);
6202 ptr = strrchrW(driver_path, '\\');
6203 if (ptr) *ptr = '\0';
6205 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6206 NULL, ODBC_INSTALL_COMPLETE, &usage))
6208 ERR("Failed to install SQL driver!\n");
6209 r = ERROR_FUNCTION_FAILED;
6212 uirow = MSI_CreateRecord( 5 );
6213 MSI_RecordSetStringW( uirow, 1, desc );
6214 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6215 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6216 msi_ui_actiondata( package, szInstallODBC, uirow );
6217 msiobj_release( &uirow->hdr );
6219 msi_free(driver);
6220 msi_free(driver_path);
6222 return r;
6225 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6227 MSIPACKAGE *package = param;
6228 LPWSTR translator, translator_path, ptr;
6229 WCHAR outpath[MAX_PATH];
6230 MSIFILE *translator_file = NULL, *setup_file = NULL;
6231 MSICOMPONENT *comp;
6232 MSIRECORD *uirow;
6233 LPCWSTR desc, file_key, component;
6234 DWORD len, usage;
6235 UINT r = ERROR_SUCCESS;
6237 static const WCHAR translator_fmt[] = {
6238 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6239 static const WCHAR setup_fmt[] = {
6240 'S','e','t','u','p','=','%','s',0};
6242 component = MSI_RecordGetString( rec, 2 );
6243 comp = msi_get_loaded_component( package, component );
6244 if (!comp)
6245 return ERROR_SUCCESS;
6247 comp->Action = msi_get_component_action( package, comp );
6248 if (comp->Action != INSTALLSTATE_LOCAL)
6250 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6251 return ERROR_SUCCESS;
6253 desc = MSI_RecordGetString(rec, 3);
6255 file_key = MSI_RecordGetString( rec, 4 );
6256 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6258 file_key = MSI_RecordGetString( rec, 5 );
6259 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6261 if (!translator_file)
6263 ERR("ODBC Translator entry not found!\n");
6264 return ERROR_FUNCTION_FAILED;
6267 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6268 if (setup_file)
6269 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6271 translator = msi_alloc(len * sizeof(WCHAR));
6272 if (!translator)
6273 return ERROR_OUTOFMEMORY;
6275 ptr = translator;
6276 lstrcpyW(ptr, desc);
6277 ptr += lstrlenW(ptr) + 1;
6279 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6280 ptr += len + 1;
6282 if (setup_file)
6284 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6285 ptr += len + 1;
6287 *ptr = '\0';
6289 translator_path = strdupW(translator_file->TargetPath);
6290 ptr = strrchrW(translator_path, '\\');
6291 if (ptr) *ptr = '\0';
6293 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6294 NULL, ODBC_INSTALL_COMPLETE, &usage))
6296 ERR("Failed to install SQL translator!\n");
6297 r = ERROR_FUNCTION_FAILED;
6300 uirow = MSI_CreateRecord( 5 );
6301 MSI_RecordSetStringW( uirow, 1, desc );
6302 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6303 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6304 msi_ui_actiondata( package, szInstallODBC, uirow );
6305 msiobj_release( &uirow->hdr );
6307 msi_free(translator);
6308 msi_free(translator_path);
6310 return r;
6313 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6315 MSIPACKAGE *package = param;
6316 MSICOMPONENT *comp;
6317 LPWSTR attrs;
6318 LPCWSTR desc, driver, component;
6319 WORD request = ODBC_ADD_SYS_DSN;
6320 INT registration;
6321 DWORD len;
6322 UINT r = ERROR_SUCCESS;
6323 MSIRECORD *uirow;
6325 static const WCHAR attrs_fmt[] = {
6326 'D','S','N','=','%','s',0 };
6328 component = MSI_RecordGetString( rec, 2 );
6329 comp = msi_get_loaded_component( package, component );
6330 if (!comp)
6331 return ERROR_SUCCESS;
6333 comp->Action = msi_get_component_action( package, comp );
6334 if (comp->Action != INSTALLSTATE_LOCAL)
6336 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6337 return ERROR_SUCCESS;
6340 desc = MSI_RecordGetString(rec, 3);
6341 driver = MSI_RecordGetString(rec, 4);
6342 registration = MSI_RecordGetInteger(rec, 5);
6344 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6345 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6347 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6348 attrs = msi_alloc(len * sizeof(WCHAR));
6349 if (!attrs)
6350 return ERROR_OUTOFMEMORY;
6352 len = sprintfW(attrs, attrs_fmt, desc);
6353 attrs[len + 1] = 0;
6355 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6357 ERR("Failed to install SQL data source!\n");
6358 r = ERROR_FUNCTION_FAILED;
6361 uirow = MSI_CreateRecord( 5 );
6362 MSI_RecordSetStringW( uirow, 1, desc );
6363 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6364 MSI_RecordSetInteger( uirow, 3, request );
6365 msi_ui_actiondata( package, szInstallODBC, uirow );
6366 msiobj_release( &uirow->hdr );
6368 msi_free(attrs);
6370 return r;
6373 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6375 static const WCHAR driver_query[] = {
6376 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6377 'O','D','B','C','D','r','i','v','e','r',0};
6378 static const WCHAR translator_query[] = {
6379 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6380 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6381 static const WCHAR source_query[] = {
6382 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6383 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6384 MSIQUERY *view;
6385 UINT rc;
6387 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6388 if (rc == ERROR_SUCCESS)
6390 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6391 msiobj_release(&view->hdr);
6392 if (rc != ERROR_SUCCESS)
6393 return rc;
6395 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6396 if (rc == ERROR_SUCCESS)
6398 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6399 msiobj_release(&view->hdr);
6400 if (rc != ERROR_SUCCESS)
6401 return rc;
6403 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6404 if (rc == ERROR_SUCCESS)
6406 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6407 msiobj_release(&view->hdr);
6408 if (rc != ERROR_SUCCESS)
6409 return rc;
6411 return ERROR_SUCCESS;
6414 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6416 MSIPACKAGE *package = param;
6417 MSICOMPONENT *comp;
6418 MSIRECORD *uirow;
6419 DWORD usage;
6420 LPCWSTR desc, component;
6422 component = MSI_RecordGetString( rec, 2 );
6423 comp = msi_get_loaded_component( package, component );
6424 if (!comp)
6425 return ERROR_SUCCESS;
6427 comp->Action = msi_get_component_action( package, comp );
6428 if (comp->Action != INSTALLSTATE_ABSENT)
6430 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6431 return ERROR_SUCCESS;
6434 desc = MSI_RecordGetString( rec, 3 );
6435 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6437 WARN("Failed to remove ODBC driver\n");
6439 else if (!usage)
6441 FIXME("Usage count reached 0\n");
6444 uirow = MSI_CreateRecord( 2 );
6445 MSI_RecordSetStringW( uirow, 1, desc );
6446 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6447 msi_ui_actiondata( package, szRemoveODBC, uirow );
6448 msiobj_release( &uirow->hdr );
6450 return ERROR_SUCCESS;
6453 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6455 MSIPACKAGE *package = param;
6456 MSICOMPONENT *comp;
6457 MSIRECORD *uirow;
6458 DWORD usage;
6459 LPCWSTR desc, component;
6461 component = MSI_RecordGetString( rec, 2 );
6462 comp = msi_get_loaded_component( package, component );
6463 if (!comp)
6464 return ERROR_SUCCESS;
6466 comp->Action = msi_get_component_action( package, comp );
6467 if (comp->Action != INSTALLSTATE_ABSENT)
6469 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6470 return ERROR_SUCCESS;
6473 desc = MSI_RecordGetString( rec, 3 );
6474 if (!SQLRemoveTranslatorW( desc, &usage ))
6476 WARN("Failed to remove ODBC translator\n");
6478 else if (!usage)
6480 FIXME("Usage count reached 0\n");
6483 uirow = MSI_CreateRecord( 2 );
6484 MSI_RecordSetStringW( uirow, 1, desc );
6485 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6486 msi_ui_actiondata( package, szRemoveODBC, uirow );
6487 msiobj_release( &uirow->hdr );
6489 return ERROR_SUCCESS;
6492 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6494 MSIPACKAGE *package = param;
6495 MSICOMPONENT *comp;
6496 MSIRECORD *uirow;
6497 LPWSTR attrs;
6498 LPCWSTR desc, driver, component;
6499 WORD request = ODBC_REMOVE_SYS_DSN;
6500 INT registration;
6501 DWORD len;
6503 static const WCHAR attrs_fmt[] = {
6504 'D','S','N','=','%','s',0 };
6506 component = MSI_RecordGetString( rec, 2 );
6507 comp = msi_get_loaded_component( package, component );
6508 if (!comp)
6509 return ERROR_SUCCESS;
6511 comp->Action = msi_get_component_action( package, comp );
6512 if (comp->Action != INSTALLSTATE_ABSENT)
6514 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6515 return ERROR_SUCCESS;
6518 desc = MSI_RecordGetString( rec, 3 );
6519 driver = MSI_RecordGetString( rec, 4 );
6520 registration = MSI_RecordGetInteger( rec, 5 );
6522 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6523 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6525 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6526 attrs = msi_alloc( len * sizeof(WCHAR) );
6527 if (!attrs)
6528 return ERROR_OUTOFMEMORY;
6530 FIXME("Use ODBCSourceAttribute table\n");
6532 len = sprintfW( attrs, attrs_fmt, desc );
6533 attrs[len + 1] = 0;
6535 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6537 WARN("Failed to remove ODBC data source\n");
6539 msi_free( attrs );
6541 uirow = MSI_CreateRecord( 3 );
6542 MSI_RecordSetStringW( uirow, 1, desc );
6543 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6544 MSI_RecordSetInteger( uirow, 3, request );
6545 msi_ui_actiondata( package, szRemoveODBC, uirow );
6546 msiobj_release( &uirow->hdr );
6548 return ERROR_SUCCESS;
6551 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6553 static const WCHAR driver_query[] = {
6554 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6555 'O','D','B','C','D','r','i','v','e','r',0};
6556 static const WCHAR translator_query[] = {
6557 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6558 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6559 static const WCHAR source_query[] = {
6560 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6561 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6562 MSIQUERY *view;
6563 UINT rc;
6565 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6566 if (rc == ERROR_SUCCESS)
6568 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6569 msiobj_release( &view->hdr );
6570 if (rc != ERROR_SUCCESS)
6571 return rc;
6573 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6574 if (rc == ERROR_SUCCESS)
6576 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6577 msiobj_release( &view->hdr );
6578 if (rc != ERROR_SUCCESS)
6579 return rc;
6581 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6582 if (rc == ERROR_SUCCESS)
6584 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6585 msiobj_release( &view->hdr );
6586 if (rc != ERROR_SUCCESS)
6587 return rc;
6589 return ERROR_SUCCESS;
6592 #define ENV_ACT_SETALWAYS 0x1
6593 #define ENV_ACT_SETABSENT 0x2
6594 #define ENV_ACT_REMOVE 0x4
6595 #define ENV_ACT_REMOVEMATCH 0x8
6597 #define ENV_MOD_MACHINE 0x20000000
6598 #define ENV_MOD_APPEND 0x40000000
6599 #define ENV_MOD_PREFIX 0x80000000
6600 #define ENV_MOD_MASK 0xC0000000
6602 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6604 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6606 LPCWSTR cptr = *name;
6608 static const WCHAR prefix[] = {'[','~',']',0};
6609 static const int prefix_len = 3;
6611 *flags = 0;
6612 while (*cptr)
6614 if (*cptr == '=')
6615 *flags |= ENV_ACT_SETALWAYS;
6616 else if (*cptr == '+')
6617 *flags |= ENV_ACT_SETABSENT;
6618 else if (*cptr == '-')
6619 *flags |= ENV_ACT_REMOVE;
6620 else if (*cptr == '!')
6621 *flags |= ENV_ACT_REMOVEMATCH;
6622 else if (*cptr == '*')
6623 *flags |= ENV_MOD_MACHINE;
6624 else
6625 break;
6627 cptr++;
6628 (*name)++;
6631 if (!*cptr)
6633 ERR("Missing environment variable\n");
6634 return ERROR_FUNCTION_FAILED;
6637 if (*value)
6639 LPCWSTR ptr = *value;
6640 if (!strncmpW(ptr, prefix, prefix_len))
6642 if (ptr[prefix_len] == szSemiColon[0])
6644 *flags |= ENV_MOD_APPEND;
6645 *value += lstrlenW(prefix);
6647 else
6649 *value = NULL;
6652 else if (lstrlenW(*value) >= prefix_len)
6654 ptr += lstrlenW(ptr) - prefix_len;
6655 if (!strcmpW( ptr, prefix ))
6657 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6659 *flags |= ENV_MOD_PREFIX;
6660 /* the "[~]" will be removed by deformat_string */;
6662 else
6664 *value = NULL;
6670 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6671 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6672 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6673 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6675 ERR("Invalid flags: %08x\n", *flags);
6676 return ERROR_FUNCTION_FAILED;
6679 if (!*flags)
6680 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6682 return ERROR_SUCCESS;
6685 static UINT open_env_key( DWORD flags, HKEY *key )
6687 static const WCHAR user_env[] =
6688 {'E','n','v','i','r','o','n','m','e','n','t',0};
6689 static const WCHAR machine_env[] =
6690 {'S','y','s','t','e','m','\\',
6691 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6692 'C','o','n','t','r','o','l','\\',
6693 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6694 'E','n','v','i','r','o','n','m','e','n','t',0};
6695 const WCHAR *env;
6696 HKEY root;
6697 LONG res;
6699 if (flags & ENV_MOD_MACHINE)
6701 env = machine_env;
6702 root = HKEY_LOCAL_MACHINE;
6704 else
6706 env = user_env;
6707 root = HKEY_CURRENT_USER;
6710 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6711 if (res != ERROR_SUCCESS)
6713 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6714 return ERROR_FUNCTION_FAILED;
6717 return ERROR_SUCCESS;
6720 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6722 MSIPACKAGE *package = param;
6723 LPCWSTR name, value, component;
6724 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6725 DWORD flags, type, size;
6726 UINT res;
6727 HKEY env = NULL;
6728 MSICOMPONENT *comp;
6729 MSIRECORD *uirow;
6730 int action = 0;
6732 component = MSI_RecordGetString(rec, 4);
6733 comp = msi_get_loaded_component(package, component);
6734 if (!comp)
6735 return ERROR_SUCCESS;
6737 comp->Action = msi_get_component_action( package, comp );
6738 if (comp->Action != INSTALLSTATE_LOCAL)
6740 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6741 return ERROR_SUCCESS;
6743 name = MSI_RecordGetString(rec, 2);
6744 value = MSI_RecordGetString(rec, 3);
6746 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6748 res = env_parse_flags(&name, &value, &flags);
6749 if (res != ERROR_SUCCESS || !value)
6750 goto done;
6752 if (value && !deformat_string(package, value, &deformatted))
6754 res = ERROR_OUTOFMEMORY;
6755 goto done;
6758 value = deformatted;
6760 res = open_env_key( flags, &env );
6761 if (res != ERROR_SUCCESS)
6762 goto done;
6764 if (flags & ENV_MOD_MACHINE)
6765 action |= 0x20000000;
6767 size = 0;
6768 type = REG_SZ;
6769 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6770 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6771 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6772 goto done;
6774 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6776 action = 0x2;
6778 /* Nothing to do. */
6779 if (!value)
6781 res = ERROR_SUCCESS;
6782 goto done;
6785 /* If we are appending but the string was empty, strip ; */
6786 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6788 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6789 newval = strdupW(value);
6790 if (!newval)
6792 res = ERROR_OUTOFMEMORY;
6793 goto done;
6796 else
6798 action = 0x1;
6800 /* Contrary to MSDN, +-variable to [~];path works */
6801 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6803 res = ERROR_SUCCESS;
6804 goto done;
6807 data = msi_alloc(size);
6808 if (!data)
6810 RegCloseKey(env);
6811 return ERROR_OUTOFMEMORY;
6814 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6815 if (res != ERROR_SUCCESS)
6816 goto done;
6818 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6820 action = 0x4;
6821 res = RegDeleteValueW(env, name);
6822 if (res != ERROR_SUCCESS)
6823 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6824 goto done;
6827 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6828 if (flags & ENV_MOD_MASK)
6830 DWORD mod_size;
6831 int multiplier = 0;
6832 if (flags & ENV_MOD_APPEND) multiplier++;
6833 if (flags & ENV_MOD_PREFIX) multiplier++;
6834 mod_size = lstrlenW(value) * multiplier;
6835 size += mod_size * sizeof(WCHAR);
6838 newval = msi_alloc(size);
6839 ptr = newval;
6840 if (!newval)
6842 res = ERROR_OUTOFMEMORY;
6843 goto done;
6846 if (flags & ENV_MOD_PREFIX)
6848 lstrcpyW(newval, value);
6849 ptr = newval + lstrlenW(value);
6850 action |= 0x80000000;
6853 lstrcpyW(ptr, data);
6855 if (flags & ENV_MOD_APPEND)
6857 lstrcatW(newval, value);
6858 action |= 0x40000000;
6861 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6862 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6863 if (res)
6865 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6868 done:
6869 uirow = MSI_CreateRecord( 3 );
6870 MSI_RecordSetStringW( uirow, 1, name );
6871 MSI_RecordSetStringW( uirow, 2, newval );
6872 MSI_RecordSetInteger( uirow, 3, action );
6873 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6874 msiobj_release( &uirow->hdr );
6876 if (env) RegCloseKey(env);
6877 msi_free(deformatted);
6878 msi_free(data);
6879 msi_free(newval);
6880 return res;
6883 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6885 static const WCHAR query[] = {
6886 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6887 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6888 MSIQUERY *view;
6889 UINT rc;
6891 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6892 if (rc != ERROR_SUCCESS)
6893 return ERROR_SUCCESS;
6895 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6896 msiobj_release(&view->hdr);
6897 return rc;
6900 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6902 MSIPACKAGE *package = param;
6903 LPCWSTR name, value, component;
6904 LPWSTR deformatted = NULL;
6905 DWORD flags;
6906 HKEY env;
6907 MSICOMPONENT *comp;
6908 MSIRECORD *uirow;
6909 int action = 0;
6910 LONG res;
6911 UINT r;
6913 component = MSI_RecordGetString( rec, 4 );
6914 comp = msi_get_loaded_component( package, component );
6915 if (!comp)
6916 return ERROR_SUCCESS;
6918 comp->Action = msi_get_component_action( package, comp );
6919 if (comp->Action != INSTALLSTATE_ABSENT)
6921 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6922 return ERROR_SUCCESS;
6924 name = MSI_RecordGetString( rec, 2 );
6925 value = MSI_RecordGetString( rec, 3 );
6927 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6929 r = env_parse_flags( &name, &value, &flags );
6930 if (r != ERROR_SUCCESS)
6931 return r;
6933 if (!(flags & ENV_ACT_REMOVE))
6935 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6936 return ERROR_SUCCESS;
6939 if (value && !deformat_string( package, value, &deformatted ))
6940 return ERROR_OUTOFMEMORY;
6942 value = deformatted;
6944 r = open_env_key( flags, &env );
6945 if (r != ERROR_SUCCESS)
6947 r = ERROR_SUCCESS;
6948 goto done;
6951 if (flags & ENV_MOD_MACHINE)
6952 action |= 0x20000000;
6954 TRACE("Removing %s\n", debugstr_w(name));
6956 res = RegDeleteValueW( env, name );
6957 if (res != ERROR_SUCCESS)
6959 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6960 r = ERROR_SUCCESS;
6963 done:
6964 uirow = MSI_CreateRecord( 3 );
6965 MSI_RecordSetStringW( uirow, 1, name );
6966 MSI_RecordSetStringW( uirow, 2, value );
6967 MSI_RecordSetInteger( uirow, 3, action );
6968 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6969 msiobj_release( &uirow->hdr );
6971 if (env) RegCloseKey( env );
6972 msi_free( deformatted );
6973 return r;
6976 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6978 static const WCHAR query[] = {
6979 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6980 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6981 MSIQUERY *view;
6982 UINT rc;
6984 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6985 if (rc != ERROR_SUCCESS)
6986 return ERROR_SUCCESS;
6988 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6989 msiobj_release( &view->hdr );
6990 return rc;
6993 UINT msi_validate_product_id( MSIPACKAGE *package )
6995 LPWSTR key, template, id;
6996 UINT r = ERROR_SUCCESS;
6998 id = msi_dup_property( package->db, szProductID );
6999 if (id)
7001 msi_free( id );
7002 return ERROR_SUCCESS;
7004 template = msi_dup_property( package->db, szPIDTemplate );
7005 key = msi_dup_property( package->db, szPIDKEY );
7006 if (key && template)
7008 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7009 r = msi_set_property( package->db, szProductID, key );
7011 msi_free( template );
7012 msi_free( key );
7013 return r;
7016 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7018 return msi_validate_product_id( package );
7021 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7023 TRACE("\n");
7024 package->need_reboot_at_end = 1;
7025 return ERROR_SUCCESS;
7028 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7030 static const WCHAR szAvailableFreeReg[] =
7031 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7032 MSIRECORD *uirow;
7033 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7035 TRACE("%p %d kilobytes\n", package, space);
7037 uirow = MSI_CreateRecord( 1 );
7038 MSI_RecordSetInteger( uirow, 1, space );
7039 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7040 msiobj_release( &uirow->hdr );
7042 return ERROR_SUCCESS;
7045 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7047 TRACE("%p\n", package);
7049 msi_set_property( package->db, szRollbackDisabled, szOne );
7050 return ERROR_SUCCESS;
7053 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7055 FIXME("%p\n", package);
7056 return ERROR_SUCCESS;
7059 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7061 static const WCHAR driver_query[] = {
7062 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7063 'O','D','B','C','D','r','i','v','e','r',0};
7064 static const WCHAR translator_query[] = {
7065 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7066 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7067 MSIQUERY *view;
7068 UINT r, count;
7070 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7071 if (r == ERROR_SUCCESS)
7073 count = 0;
7074 r = MSI_IterateRecords( view, &count, NULL, package );
7075 msiobj_release( &view->hdr );
7076 if (r != ERROR_SUCCESS)
7077 return r;
7078 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7080 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7081 if (r == ERROR_SUCCESS)
7083 count = 0;
7084 r = MSI_IterateRecords( view, &count, NULL, package );
7085 msiobj_release( &view->hdr );
7086 if (r != ERROR_SUCCESS)
7087 return r;
7088 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7090 return ERROR_SUCCESS;
7093 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7095 static const WCHAR fmtW[] =
7096 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7097 MSIPACKAGE *package = param;
7098 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7099 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7100 WCHAR *product, *features, *cmd;
7101 STARTUPINFOW si;
7102 PROCESS_INFORMATION info;
7103 BOOL ret;
7105 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7107 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7109 len += strlenW( product );
7110 if (features)
7111 len += strlenW( features );
7112 else
7113 len += sizeof(szAll) / sizeof(szAll[0]);
7115 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7117 msi_free( product );
7118 msi_free( features );
7119 return ERROR_OUTOFMEMORY;
7121 sprintfW( cmd, fmtW, product, features ? features : szAll );
7122 msi_free( product );
7123 msi_free( features );
7125 memset( &si, 0, sizeof(STARTUPINFOW) );
7126 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7127 msi_free( cmd );
7128 if (!ret) return GetLastError();
7129 CloseHandle( info.hThread );
7131 WaitForSingleObject( info.hProcess, INFINITE );
7132 CloseHandle( info.hProcess );
7133 return ERROR_SUCCESS;
7136 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7138 static const WCHAR query[] = {
7139 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7140 MSIQUERY *view;
7141 UINT r;
7143 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7144 if (r == ERROR_SUCCESS)
7146 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7147 msiobj_release( &view->hdr );
7148 if (r != ERROR_SUCCESS)
7149 return r;
7151 return ERROR_SUCCESS;
7154 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7156 MSIPACKAGE *package = param;
7157 int attributes = MSI_RecordGetInteger( rec, 5 );
7159 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7161 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7162 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7163 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7164 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7165 HKEY hkey;
7166 UINT r;
7168 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7170 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7171 if (r != ERROR_SUCCESS)
7172 return ERROR_SUCCESS;
7174 else
7176 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7177 if (r != ERROR_SUCCESS)
7178 return ERROR_SUCCESS;
7180 RegCloseKey( hkey );
7182 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7183 debugstr_w(upgrade_code), debugstr_w(version_min),
7184 debugstr_w(version_max), debugstr_w(language));
7186 return ERROR_SUCCESS;
7189 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7191 static const WCHAR query[] = {
7192 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7193 'U','p','g','r','a','d','e',0};
7194 MSIQUERY *view;
7195 UINT r;
7197 if (msi_get_property_int( package->db, szInstalled, 0 ))
7199 TRACE("product is installed, skipping action\n");
7200 return ERROR_SUCCESS;
7202 if (msi_get_property_int( package->db, szPreselected, 0 ))
7204 TRACE("Preselected property is set, not migrating feature states\n");
7205 return ERROR_SUCCESS;
7207 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7208 if (r == ERROR_SUCCESS)
7210 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7211 msiobj_release( &view->hdr );
7212 if (r != ERROR_SUCCESS)
7213 return r;
7215 return ERROR_SUCCESS;
7218 static void bind_image( const char *filename, const char *path )
7220 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7222 WARN("failed to bind image %u\n", GetLastError());
7226 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7228 UINT i;
7229 MSIFILE *file;
7230 MSIPACKAGE *package = param;
7231 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7232 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7233 char *filenameA, *pathA;
7234 WCHAR *pathW, **path_list;
7236 if (!(file = msi_get_loaded_file( package, key )))
7238 WARN("file %s not found\n", debugstr_w(key));
7239 return ERROR_SUCCESS;
7241 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7242 path_list = msi_split_string( paths, ';' );
7243 if (!path_list) bind_image( filenameA, NULL );
7244 else
7246 for (i = 0; path_list[i] && path_list[i][0]; i++)
7248 deformat_string( package, path_list[i], &pathW );
7249 if ((pathA = strdupWtoA( pathW )))
7251 bind_image( filenameA, pathA );
7252 msi_free( pathA );
7254 msi_free( pathW );
7257 msi_free( path_list );
7258 msi_free( filenameA );
7259 return ERROR_SUCCESS;
7262 static UINT ACTION_BindImage( MSIPACKAGE *package )
7264 static const WCHAR query[] = {
7265 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7266 'B','i','n','d','I','m','a','g','e',0};
7267 MSIQUERY *view;
7268 UINT r;
7270 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7271 if (r == ERROR_SUCCESS)
7273 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7274 msiobj_release( &view->hdr );
7275 if (r != ERROR_SUCCESS)
7276 return r;
7278 return ERROR_SUCCESS;
7281 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7283 static const WCHAR query[] = {
7284 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7285 MSIQUERY *view;
7286 DWORD count = 0;
7287 UINT r;
7289 r = MSI_OpenQuery( package->db, &view, query, table );
7290 if (r == ERROR_SUCCESS)
7292 r = MSI_IterateRecords(view, &count, NULL, package);
7293 msiobj_release(&view->hdr);
7294 if (r != ERROR_SUCCESS)
7295 return r;
7297 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7298 return ERROR_SUCCESS;
7301 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7303 static const WCHAR table[] = {
7304 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7305 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7308 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7310 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7311 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7314 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7316 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7317 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7320 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7322 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7323 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7326 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7328 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7329 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7332 static const struct
7334 const WCHAR *action;
7335 UINT (*handler)(MSIPACKAGE *);
7336 const WCHAR *action_rollback;
7338 StandardActions[] =
7340 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7341 { szAppSearch, ACTION_AppSearch, NULL },
7342 { szBindImage, ACTION_BindImage, NULL },
7343 { szCCPSearch, ACTION_CCPSearch, NULL },
7344 { szCostFinalize, ACTION_CostFinalize, NULL },
7345 { szCostInitialize, ACTION_CostInitialize, NULL },
7346 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7347 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7348 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7349 { szDisableRollback, ACTION_DisableRollback, NULL },
7350 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7351 { szExecuteAction, ACTION_ExecuteAction, NULL },
7352 { szFileCost, ACTION_FileCost, NULL },
7353 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7354 { szForceReboot, ACTION_ForceReboot, NULL },
7355 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7356 { szInstallExecute, ACTION_InstallExecute, NULL },
7357 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7358 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7359 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7360 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7361 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7362 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7363 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7364 { szInstallValidate, ACTION_InstallValidate, NULL },
7365 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7366 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7367 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7368 { szMoveFiles, ACTION_MoveFiles, NULL },
7369 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7370 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7371 { szPatchFiles, ACTION_PatchFiles, NULL },
7372 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7373 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7374 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7375 { szPublishProduct, ACTION_PublishProduct, NULL },
7376 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7377 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7378 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7379 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7380 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7381 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7382 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7383 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7384 { szRegisterUser, ACTION_RegisterUser, NULL },
7385 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7386 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7387 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7388 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7389 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7390 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7391 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7392 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7393 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7394 { szResolveSource, ACTION_ResolveSource, NULL },
7395 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7396 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7397 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7398 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7399 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7400 { szStartServices, ACTION_StartServices, szStopServices },
7401 { szStopServices, ACTION_StopServices, szStartServices },
7402 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7403 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7404 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7405 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7406 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7407 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7408 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7409 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7410 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7411 { szValidateProductID, ACTION_ValidateProductID, NULL },
7412 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7413 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7414 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7415 { NULL, NULL, NULL }
7418 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7420 BOOL ret = FALSE;
7421 UINT i;
7423 i = 0;
7424 while (StandardActions[i].action != NULL)
7426 if (!strcmpW( StandardActions[i].action, action ))
7428 ui_actionstart( package, action );
7429 if (StandardActions[i].handler)
7431 ui_actioninfo( package, action, TRUE, 0 );
7432 *rc = StandardActions[i].handler( package );
7433 ui_actioninfo( package, action, FALSE, *rc );
7435 if (StandardActions[i].action_rollback && !package->need_rollback)
7437 TRACE("scheduling rollback action\n");
7438 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7441 else
7443 FIXME("unhandled standard action %s\n", debugstr_w(action));
7444 *rc = ERROR_SUCCESS;
7446 ret = TRUE;
7447 break;
7449 i++;
7451 return ret;
7454 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7456 UINT rc = ERROR_SUCCESS;
7457 BOOL handled;
7459 TRACE("Performing action (%s)\n", debugstr_w(action));
7461 handled = ACTION_HandleStandardAction(package, action, &rc);
7463 if (!handled)
7464 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7466 if (!handled)
7468 WARN("unhandled msi action %s\n", debugstr_w(action));
7469 rc = ERROR_FUNCTION_NOT_CALLED;
7472 return rc;
7475 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7477 UINT rc = ERROR_SUCCESS;
7478 BOOL handled = FALSE;
7480 TRACE("Performing action (%s)\n", debugstr_w(action));
7482 package->action_progress_increment = 0;
7483 handled = ACTION_HandleStandardAction(package, action, &rc);
7485 if (!handled)
7486 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7488 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7489 handled = TRUE;
7491 if (!handled)
7493 WARN("unhandled msi action %s\n", debugstr_w(action));
7494 rc = ERROR_FUNCTION_NOT_CALLED;
7497 return rc;
7500 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7502 UINT rc = ERROR_SUCCESS;
7503 MSIRECORD *row;
7505 static const WCHAR query[] =
7506 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7507 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7508 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7509 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7510 static const WCHAR ui_query[] =
7511 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7512 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7513 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7514 ' ', '=',' ','%','i',0};
7516 if (needs_ui_sequence(package))
7517 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7518 else
7519 row = MSI_QueryGetRecord(package->db, query, seq);
7521 if (row)
7523 LPCWSTR action, cond;
7525 TRACE("Running the actions\n");
7527 /* check conditions */
7528 cond = MSI_RecordGetString(row, 2);
7530 /* this is a hack to skip errors in the condition code */
7531 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7533 msiobj_release(&row->hdr);
7534 return ERROR_SUCCESS;
7537 action = MSI_RecordGetString(row, 1);
7538 if (!action)
7540 ERR("failed to fetch action\n");
7541 msiobj_release(&row->hdr);
7542 return ERROR_FUNCTION_FAILED;
7545 if (needs_ui_sequence(package))
7546 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7547 else
7548 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7550 msiobj_release(&row->hdr);
7553 return rc;
7556 /****************************************************
7557 * TOP level entry points
7558 *****************************************************/
7560 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7561 LPCWSTR szCommandLine )
7563 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7564 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7565 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7566 WCHAR *reinstall = NULL;
7567 BOOL ui_exists;
7568 UINT rc;
7570 msi_set_property( package->db, szAction, szInstall );
7572 package->script->InWhatSequence = SEQUENCE_INSTALL;
7574 if (szPackagePath)
7576 LPWSTR p, dir;
7577 LPCWSTR file;
7579 dir = strdupW(szPackagePath);
7580 p = strrchrW(dir, '\\');
7581 if (p)
7583 *(++p) = 0;
7584 file = szPackagePath + (p - dir);
7586 else
7588 msi_free(dir);
7589 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7590 GetCurrentDirectoryW(MAX_PATH, dir);
7591 lstrcatW(dir, szBackSlash);
7592 file = szPackagePath;
7595 msi_free( package->PackagePath );
7596 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7597 if (!package->PackagePath)
7599 msi_free(dir);
7600 return ERROR_OUTOFMEMORY;
7603 lstrcpyW(package->PackagePath, dir);
7604 lstrcatW(package->PackagePath, file);
7605 msi_free(dir);
7607 msi_set_sourcedir_props(package, FALSE);
7610 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7611 if (rc != ERROR_SUCCESS)
7612 return rc;
7614 msi_apply_transforms( package );
7615 msi_apply_patches( package );
7617 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7619 TRACE("setting reinstall property\n");
7620 msi_set_property( package->db, szReinstall, szAll );
7623 /* properties may have been added by a transform */
7624 msi_clone_properties( package );
7626 msi_parse_command_line( package, szCommandLine, FALSE );
7627 msi_adjust_privilege_properties( package );
7628 msi_set_context( package );
7630 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7632 TRACE("disabling rollback\n");
7633 msi_set_property( package->db, szRollbackDisabled, szOne );
7636 if (needs_ui_sequence( package))
7638 package->script->InWhatSequence |= SEQUENCE_UI;
7639 rc = ACTION_ProcessUISequence(package);
7640 ui_exists = ui_sequence_exists(package);
7641 if (rc == ERROR_SUCCESS || !ui_exists)
7643 package->script->InWhatSequence |= SEQUENCE_EXEC;
7644 rc = ACTION_ProcessExecSequence(package, ui_exists);
7647 else
7648 rc = ACTION_ProcessExecSequence(package, FALSE);
7650 package->script->CurrentlyScripting = FALSE;
7652 /* process the ending type action */
7653 if (rc == ERROR_SUCCESS)
7654 ACTION_PerformActionSequence(package, -1);
7655 else if (rc == ERROR_INSTALL_USEREXIT)
7656 ACTION_PerformActionSequence(package, -2);
7657 else if (rc == ERROR_INSTALL_SUSPEND)
7658 ACTION_PerformActionSequence(package, -4);
7659 else /* failed */
7661 ACTION_PerformActionSequence(package, -3);
7662 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7664 package->need_rollback = TRUE;
7668 /* finish up running custom actions */
7669 ACTION_FinishCustomActions(package);
7671 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7673 WARN("installation failed, running rollback script\n");
7674 execute_script( package, SCRIPT_ROLLBACK );
7676 msi_free( reinstall );
7678 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7679 return ERROR_SUCCESS_REBOOT_REQUIRED;
7681 return rc;