msi: Handle more than one patch per file.
[wine/multimedia.git] / dlls / msi / action.c
blob827a0c86012598f297ba04205c42d87b5834ead3
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, -1 );
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, -1 );
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, -1 );
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, -1 );
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 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
689 MSIFOLDER *folder;
691 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
693 if (!strcmpW( dir, folder->Directory )) return folder;
695 return NULL;
699 * Recursively create all directories in the path.
700 * shamelessly stolen from setupapi/queue.c
702 BOOL msi_create_full_path( const WCHAR *path )
704 BOOL ret = TRUE;
705 WCHAR *new_path;
706 int len;
708 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
709 strcpyW( new_path, path );
711 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
712 new_path[len - 1] = 0;
714 while (!CreateDirectoryW( new_path, NULL ))
716 WCHAR *slash;
717 DWORD last_error = GetLastError();
718 if (last_error == ERROR_ALREADY_EXISTS) break;
719 if (last_error != ERROR_PATH_NOT_FOUND)
721 ret = FALSE;
722 break;
724 if (!(slash = strrchrW( new_path, '\\' )))
726 ret = FALSE;
727 break;
729 len = slash - new_path;
730 new_path[len] = 0;
731 if (!msi_create_full_path( new_path ))
733 ret = FALSE;
734 break;
736 new_path[len] = '\\';
738 msi_free( new_path );
739 return ret;
742 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
744 MSIRECORD *row;
746 row = MSI_CreateRecord( 4 );
747 MSI_RecordSetInteger( row, 1, a );
748 MSI_RecordSetInteger( row, 2, b );
749 MSI_RecordSetInteger( row, 3, c );
750 MSI_RecordSetInteger( row, 4, d );
751 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
752 msiobj_release( &row->hdr );
754 msi_dialog_check_messages( NULL );
757 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
759 static const WCHAR query[] =
760 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
761 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
762 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
763 WCHAR message[1024];
764 MSIRECORD *row = 0;
765 DWORD size;
767 if (!package->LastAction || strcmpW( package->LastAction, action ))
769 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
771 if (MSI_RecordIsNull( row, 3 ))
773 msiobj_release( &row->hdr );
774 return;
776 /* update the cached action format */
777 msi_free( package->ActionFormat );
778 package->ActionFormat = msi_dup_record_field( row, 3 );
779 msi_free( package->LastAction );
780 package->LastAction = strdupW( action );
781 msiobj_release( &row->hdr );
783 size = 1024;
784 MSI_RecordSetStringW( record, 0, package->ActionFormat );
785 MSI_FormatRecordW( package, record, message, &size );
786 row = MSI_CreateRecord( 1 );
787 MSI_RecordSetStringW( row, 1, message );
788 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
789 msiobj_release( &row->hdr );
792 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
794 if (!comp->Enabled)
796 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
797 return INSTALLSTATE_UNKNOWN;
799 if (package->need_rollback) return comp->Installed;
800 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
802 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
803 return INSTALLSTATE_UNKNOWN;
805 return comp->ActionRequest;
808 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
810 if (package->need_rollback) return feature->Installed;
811 return feature->ActionRequest;
814 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
816 MSIPACKAGE *package = param;
817 LPCWSTR dir, component, full_path;
818 MSIRECORD *uirow;
819 MSIFOLDER *folder;
820 MSICOMPONENT *comp;
822 component = MSI_RecordGetString(row, 2);
823 if (!component)
824 return ERROR_SUCCESS;
826 comp = msi_get_loaded_component(package, component);
827 if (!comp)
828 return ERROR_SUCCESS;
830 comp->Action = msi_get_component_action( package, comp );
831 if (comp->Action != INSTALLSTATE_LOCAL)
833 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
834 return ERROR_SUCCESS;
837 dir = MSI_RecordGetString(row,1);
838 if (!dir)
840 ERR("Unable to get folder id\n");
841 return ERROR_SUCCESS;
844 uirow = MSI_CreateRecord(1);
845 MSI_RecordSetStringW(uirow, 1, dir);
846 msi_ui_actiondata(package, szCreateFolders, uirow);
847 msiobj_release(&uirow->hdr);
849 full_path = msi_get_target_folder( package, dir );
850 if (!full_path)
852 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
853 return ERROR_SUCCESS;
855 TRACE("folder is %s\n", debugstr_w(full_path));
857 folder = msi_get_loaded_folder( package, dir );
858 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
859 folder->State = FOLDER_STATE_CREATED;
860 return ERROR_SUCCESS;
863 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
865 static const WCHAR query[] = {
866 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
867 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
868 MSIQUERY *view;
869 UINT rc;
871 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
872 if (rc != ERROR_SUCCESS)
873 return ERROR_SUCCESS;
875 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
876 msiobj_release(&view->hdr);
877 return rc;
880 static void remove_persistent_folder( MSIFOLDER *folder )
882 FolderList *fl;
884 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
886 remove_persistent_folder( fl->folder );
888 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
890 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
894 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
896 MSIPACKAGE *package = param;
897 LPCWSTR dir, component, full_path;
898 MSIRECORD *uirow;
899 MSIFOLDER *folder;
900 MSICOMPONENT *comp;
902 component = MSI_RecordGetString(row, 2);
903 if (!component)
904 return ERROR_SUCCESS;
906 comp = msi_get_loaded_component(package, component);
907 if (!comp)
908 return ERROR_SUCCESS;
910 comp->Action = msi_get_component_action( package, comp );
911 if (comp->Action != INSTALLSTATE_ABSENT)
913 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
914 return ERROR_SUCCESS;
917 dir = MSI_RecordGetString( row, 1 );
918 if (!dir)
920 ERR("Unable to get folder id\n");
921 return ERROR_SUCCESS;
924 full_path = msi_get_target_folder( package, dir );
925 if (!full_path)
927 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
928 return ERROR_SUCCESS;
930 TRACE("folder is %s\n", debugstr_w(full_path));
932 uirow = MSI_CreateRecord( 1 );
933 MSI_RecordSetStringW( uirow, 1, dir );
934 msi_ui_actiondata( package, szRemoveFolders, uirow );
935 msiobj_release( &uirow->hdr );
937 folder = msi_get_loaded_folder( package, dir );
938 remove_persistent_folder( folder );
939 return ERROR_SUCCESS;
942 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
944 static const WCHAR query[] = {
945 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
946 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
947 MSIQUERY *view;
948 UINT rc;
950 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
951 if (rc != ERROR_SUCCESS)
952 return ERROR_SUCCESS;
954 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
955 msiobj_release( &view->hdr );
956 return rc;
959 static UINT load_component( MSIRECORD *row, LPVOID param )
961 MSIPACKAGE *package = param;
962 MSICOMPONENT *comp;
964 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
965 if (!comp)
966 return ERROR_FUNCTION_FAILED;
968 list_add_tail( &package->components, &comp->entry );
970 /* fill in the data */
971 comp->Component = msi_dup_record_field( row, 1 );
973 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
975 comp->ComponentId = msi_dup_record_field( row, 2 );
976 comp->Directory = msi_dup_record_field( row, 3 );
977 comp->Attributes = MSI_RecordGetInteger(row,4);
978 comp->Condition = msi_dup_record_field( row, 5 );
979 comp->KeyPath = msi_dup_record_field( row, 6 );
981 comp->Installed = INSTALLSTATE_UNKNOWN;
982 comp->Action = INSTALLSTATE_UNKNOWN;
983 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
985 comp->assembly = msi_load_assembly( package, comp );
986 return ERROR_SUCCESS;
989 UINT msi_load_all_components( MSIPACKAGE *package )
991 static const WCHAR query[] = {
992 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
993 '`','C','o','m','p','o','n','e','n','t','`',0};
994 MSIQUERY *view;
995 UINT r;
997 if (!list_empty(&package->components))
998 return ERROR_SUCCESS;
1000 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1001 if (r != ERROR_SUCCESS)
1002 return r;
1004 if (!msi_init_assembly_caches( package ))
1006 ERR("can't initialize assembly caches\n");
1007 msiobj_release( &view->hdr );
1008 return ERROR_FUNCTION_FAILED;
1011 r = MSI_IterateRecords(view, NULL, load_component, package);
1012 msiobj_release(&view->hdr);
1013 return r;
1016 typedef struct {
1017 MSIPACKAGE *package;
1018 MSIFEATURE *feature;
1019 } _ilfs;
1021 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1023 ComponentList *cl;
1025 cl = msi_alloc( sizeof (*cl) );
1026 if ( !cl )
1027 return ERROR_NOT_ENOUGH_MEMORY;
1028 cl->component = comp;
1029 list_add_tail( &feature->Components, &cl->entry );
1031 return ERROR_SUCCESS;
1034 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1036 FeatureList *fl;
1038 fl = msi_alloc( sizeof(*fl) );
1039 if ( !fl )
1040 return ERROR_NOT_ENOUGH_MEMORY;
1041 fl->feature = child;
1042 list_add_tail( &parent->Children, &fl->entry );
1044 return ERROR_SUCCESS;
1047 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1049 _ilfs* ilfs = param;
1050 LPCWSTR component;
1051 MSICOMPONENT *comp;
1053 component = MSI_RecordGetString(row,1);
1055 /* check to see if the component is already loaded */
1056 comp = msi_get_loaded_component( ilfs->package, component );
1057 if (!comp)
1059 WARN("ignoring unknown component %s\n", debugstr_w(component));
1060 return ERROR_SUCCESS;
1062 add_feature_component( ilfs->feature, comp );
1063 comp->Enabled = TRUE;
1065 return ERROR_SUCCESS;
1068 static UINT load_feature(MSIRECORD * row, LPVOID param)
1070 static const WCHAR query[] = {
1071 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1072 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1073 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1074 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1075 MSIPACKAGE *package = param;
1076 MSIFEATURE *feature;
1077 MSIQUERY *view;
1078 _ilfs ilfs;
1079 UINT rc;
1081 /* fill in the data */
1083 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1084 if (!feature)
1085 return ERROR_NOT_ENOUGH_MEMORY;
1087 list_init( &feature->Children );
1088 list_init( &feature->Components );
1090 feature->Feature = msi_dup_record_field( row, 1 );
1092 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1094 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1095 feature->Title = msi_dup_record_field( row, 3 );
1096 feature->Description = msi_dup_record_field( row, 4 );
1098 if (!MSI_RecordIsNull(row,5))
1099 feature->Display = MSI_RecordGetInteger(row,5);
1101 feature->Level= MSI_RecordGetInteger(row,6);
1102 feature->Directory = msi_dup_record_field( row, 7 );
1103 feature->Attributes = MSI_RecordGetInteger(row,8);
1105 feature->Installed = INSTALLSTATE_UNKNOWN;
1106 feature->Action = INSTALLSTATE_UNKNOWN;
1107 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1109 list_add_tail( &package->features, &feature->entry );
1111 /* load feature components */
1113 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1114 if (rc != ERROR_SUCCESS)
1115 return ERROR_SUCCESS;
1117 ilfs.package = package;
1118 ilfs.feature = feature;
1120 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1121 msiobj_release(&view->hdr);
1122 return rc;
1125 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1127 MSIPACKAGE *package = param;
1128 MSIFEATURE *parent, *child;
1130 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1131 if (!child)
1132 return ERROR_FUNCTION_FAILED;
1134 if (!child->Feature_Parent)
1135 return ERROR_SUCCESS;
1137 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1138 if (!parent)
1139 return ERROR_FUNCTION_FAILED;
1141 add_feature_child( parent, child );
1142 return ERROR_SUCCESS;
1145 UINT msi_load_all_features( MSIPACKAGE *package )
1147 static const WCHAR query[] = {
1148 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1149 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1150 '`','D','i','s','p','l','a','y','`',0};
1151 MSIQUERY *view;
1152 UINT r;
1154 if (!list_empty(&package->features))
1155 return ERROR_SUCCESS;
1157 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1158 if (r != ERROR_SUCCESS)
1159 return r;
1161 r = MSI_IterateRecords( view, NULL, load_feature, package );
1162 if (r != ERROR_SUCCESS)
1164 msiobj_release( &view->hdr );
1165 return r;
1167 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1168 msiobj_release( &view->hdr );
1169 return r;
1172 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1174 if (!p)
1175 return p;
1176 p = strchrW(p, ch);
1177 if (!p)
1178 return p;
1179 *p = 0;
1180 return p+1;
1183 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1185 static const WCHAR query[] = {
1186 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1187 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1188 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1189 MSIQUERY *view = NULL;
1190 MSIRECORD *row = NULL;
1191 UINT r;
1193 TRACE("%s\n", debugstr_w(file->File));
1195 r = MSI_OpenQuery(package->db, &view, query, file->File);
1196 if (r != ERROR_SUCCESS)
1197 goto done;
1199 r = MSI_ViewExecute(view, NULL);
1200 if (r != ERROR_SUCCESS)
1201 goto done;
1203 r = MSI_ViewFetch(view, &row);
1204 if (r != ERROR_SUCCESS)
1205 goto done;
1207 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1208 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1209 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1210 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1211 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1213 done:
1214 if (view) msiobj_release(&view->hdr);
1215 if (row) msiobj_release(&row->hdr);
1216 return r;
1219 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1221 MSIRECORD *row;
1222 static const WCHAR query[] = {
1223 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1224 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1225 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1227 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1228 if (!row)
1230 WARN("query failed\n");
1231 return ERROR_FUNCTION_FAILED;
1234 file->disk_id = MSI_RecordGetInteger( row, 1 );
1235 msiobj_release( &row->hdr );
1236 return ERROR_SUCCESS;
1239 static UINT load_file(MSIRECORD *row, LPVOID param)
1241 MSIPACKAGE* package = param;
1242 LPCWSTR component;
1243 MSIFILE *file;
1245 /* fill in the data */
1247 file = msi_alloc_zero( sizeof (MSIFILE) );
1248 if (!file)
1249 return ERROR_NOT_ENOUGH_MEMORY;
1251 file->File = msi_dup_record_field( row, 1 );
1253 component = MSI_RecordGetString( row, 2 );
1254 file->Component = msi_get_loaded_component( package, component );
1256 if (!file->Component)
1258 WARN("Component not found: %s\n", debugstr_w(component));
1259 msi_free(file->File);
1260 msi_free(file);
1261 return ERROR_SUCCESS;
1264 file->FileName = msi_dup_record_field( row, 3 );
1265 msi_reduce_to_long_filename( file->FileName );
1267 file->ShortName = msi_dup_record_field( row, 3 );
1268 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1270 file->FileSize = MSI_RecordGetInteger( row, 4 );
1271 file->Version = msi_dup_record_field( row, 5 );
1272 file->Language = msi_dup_record_field( row, 6 );
1273 file->Attributes = MSI_RecordGetInteger( row, 7 );
1274 file->Sequence = MSI_RecordGetInteger( row, 8 );
1276 file->state = msifs_invalid;
1278 /* if the compressed bits are not set in the file attributes,
1279 * then read the information from the package word count property
1281 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1283 file->IsCompressed = FALSE;
1285 else if (file->Attributes &
1286 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1288 file->IsCompressed = TRUE;
1290 else if (file->Attributes & msidbFileAttributesNoncompressed)
1292 file->IsCompressed = FALSE;
1294 else
1296 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1299 load_file_hash(package, file);
1300 load_file_disk_id(package, file);
1302 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1304 list_add_tail( &package->files, &file->entry );
1306 return ERROR_SUCCESS;
1309 static UINT load_all_files(MSIPACKAGE *package)
1311 static const WCHAR query[] = {
1312 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1313 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1314 '`','S','e','q','u','e','n','c','e','`', 0};
1315 MSIQUERY *view;
1316 UINT rc;
1318 if (!list_empty(&package->files))
1319 return ERROR_SUCCESS;
1321 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1322 if (rc != ERROR_SUCCESS)
1323 return ERROR_SUCCESS;
1325 rc = MSI_IterateRecords(view, NULL, load_file, package);
1326 msiobj_release(&view->hdr);
1327 return rc;
1330 static UINT load_media( MSIRECORD *row, LPVOID param )
1332 MSIPACKAGE *package = param;
1333 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1334 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1336 /* FIXME: load external cabinets and directory sources too */
1337 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1338 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1339 return ERROR_SUCCESS;
1342 static UINT load_all_media( MSIPACKAGE *package )
1344 static const WCHAR query[] = {
1345 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1346 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1347 '`','D','i','s','k','I','d','`',0};
1348 MSIQUERY *view;
1349 UINT r;
1351 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1352 if (r != ERROR_SUCCESS)
1353 return ERROR_SUCCESS;
1355 r = MSI_IterateRecords( view, NULL, load_media, package );
1356 msiobj_release( &view->hdr );
1357 return r;
1360 static UINT load_patch(MSIRECORD *row, LPVOID param)
1362 MSIPACKAGE *package = param;
1363 MSIFILEPATCH *patch;
1364 LPWSTR file_key;
1366 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1367 if (!patch)
1368 return ERROR_NOT_ENOUGH_MEMORY;
1370 file_key = msi_dup_record_field( row, 1 );
1371 patch->File = msi_get_loaded_file( package, file_key );
1372 msi_free(file_key);
1374 if( !patch->File )
1376 ERR("Failed to find target for patch in File table\n");
1377 msi_free(patch);
1378 return ERROR_FUNCTION_FAILED;
1381 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1383 /* FIXME: The database should be properly transformed */
1384 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1386 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1387 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1388 patch->IsApplied = FALSE;
1390 /* FIXME:
1391 * Header field - for patch validation.
1392 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1395 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1397 list_add_tail( &package->filepatches, &patch->entry );
1399 return ERROR_SUCCESS;
1402 static UINT load_all_patches(MSIPACKAGE *package)
1404 static const WCHAR query[] = {
1405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1406 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1407 '`','S','e','q','u','e','n','c','e','`',0};
1408 MSIQUERY *view;
1409 UINT rc;
1411 if (!list_empty(&package->filepatches))
1412 return ERROR_SUCCESS;
1414 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1415 if (rc != ERROR_SUCCESS)
1416 return ERROR_SUCCESS;
1418 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1419 msiobj_release(&view->hdr);
1420 return rc;
1423 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1425 static const WCHAR query[] = {
1426 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1427 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1428 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1429 MSIQUERY *view;
1431 folder->persistent = FALSE;
1432 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1434 if (!MSI_ViewExecute( view, NULL ))
1436 MSIRECORD *rec;
1437 if (!MSI_ViewFetch( view, &rec ))
1439 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1440 folder->persistent = TRUE;
1441 msiobj_release( &rec->hdr );
1444 msiobj_release( &view->hdr );
1446 return ERROR_SUCCESS;
1449 static UINT load_folder( MSIRECORD *row, LPVOID param )
1451 MSIPACKAGE *package = param;
1452 static WCHAR szEmpty[] = { 0 };
1453 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1454 MSIFOLDER *folder;
1456 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1457 list_init( &folder->children );
1458 folder->Directory = msi_dup_record_field( row, 1 );
1459 folder->Parent = msi_dup_record_field( row, 2 );
1460 p = msi_dup_record_field(row, 3);
1462 TRACE("%s\n", debugstr_w(folder->Directory));
1464 /* split src and target dir */
1465 tgt_short = p;
1466 src_short = folder_split_path( p, ':' );
1468 /* split the long and short paths */
1469 tgt_long = folder_split_path( tgt_short, '|' );
1470 src_long = folder_split_path( src_short, '|' );
1472 /* check for no-op dirs */
1473 if (tgt_short && !strcmpW( szDot, tgt_short ))
1474 tgt_short = szEmpty;
1475 if (src_short && !strcmpW( szDot, src_short ))
1476 src_short = szEmpty;
1478 if (!tgt_long)
1479 tgt_long = tgt_short;
1481 if (!src_short) {
1482 src_short = tgt_short;
1483 src_long = tgt_long;
1486 if (!src_long)
1487 src_long = src_short;
1489 /* FIXME: use the target short path too */
1490 folder->TargetDefault = strdupW(tgt_long);
1491 folder->SourceShortPath = strdupW(src_short);
1492 folder->SourceLongPath = strdupW(src_long);
1493 msi_free(p);
1495 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1496 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1497 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1499 load_folder_persistence( package, folder );
1501 list_add_tail( &package->folders, &folder->entry );
1502 return ERROR_SUCCESS;
1505 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1507 FolderList *fl;
1509 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1510 fl->folder = child;
1511 list_add_tail( &parent->children, &fl->entry );
1512 return ERROR_SUCCESS;
1515 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1517 MSIPACKAGE *package = param;
1518 MSIFOLDER *parent, *child;
1520 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1521 return ERROR_FUNCTION_FAILED;
1523 if (!child->Parent) return ERROR_SUCCESS;
1525 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1526 return ERROR_FUNCTION_FAILED;
1528 return add_folder_child( parent, child );
1531 static UINT load_all_folders( MSIPACKAGE *package )
1533 static const WCHAR query[] = {
1534 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1535 '`','D','i','r','e','c','t','o','r','y','`',0};
1536 MSIQUERY *view;
1537 UINT r;
1539 if (!list_empty(&package->folders))
1540 return ERROR_SUCCESS;
1542 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1543 if (r != ERROR_SUCCESS)
1544 return r;
1546 r = MSI_IterateRecords( view, NULL, load_folder, package );
1547 if (r != ERROR_SUCCESS)
1549 msiobj_release( &view->hdr );
1550 return r;
1552 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1553 msiobj_release( &view->hdr );
1554 return r;
1557 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1559 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1560 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1562 load_all_folders( package );
1563 msi_load_all_components( package );
1564 msi_load_all_features( package );
1565 load_all_files( package );
1566 load_all_patches( package );
1567 load_all_media( package );
1569 return ERROR_SUCCESS;
1572 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1574 const WCHAR *action = package->script->Actions[script][index];
1575 ui_actionstart( package, action );
1576 TRACE("executing %s\n", debugstr_w(action));
1577 return ACTION_PerformAction( package, action, script );
1580 static UINT execute_script( MSIPACKAGE *package, UINT script )
1582 UINT i, rc = ERROR_SUCCESS;
1584 TRACE("executing script %u\n", script);
1586 if (!package->script)
1588 ERR("no script!\n");
1589 return ERROR_FUNCTION_FAILED;
1591 if (script == SCRIPT_ROLLBACK)
1593 for (i = package->script->ActionCount[script]; i > 0; i--)
1595 rc = execute_script_action( package, script, i - 1 );
1596 if (rc != ERROR_SUCCESS) break;
1599 else
1601 for (i = 0; i < package->script->ActionCount[script]; i++)
1603 rc = execute_script_action( package, script, i );
1604 if (rc != ERROR_SUCCESS) break;
1607 msi_free_action_script(package, script);
1608 return rc;
1611 static UINT ACTION_FileCost(MSIPACKAGE *package)
1613 return ERROR_SUCCESS;
1616 static void get_client_counts( MSIPACKAGE *package )
1618 MSICOMPONENT *comp;
1619 HKEY hkey;
1621 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1623 if (!comp->ComponentId) continue;
1625 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1626 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1628 comp->num_clients = 0;
1629 continue;
1631 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1632 NULL, NULL, NULL, NULL );
1633 RegCloseKey( hkey );
1637 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1639 MSICOMPONENT *comp;
1640 UINT r;
1642 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1644 if (!comp->ComponentId) continue;
1646 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1647 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1648 &comp->Installed );
1649 if (r == ERROR_SUCCESS) continue;
1651 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1652 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1653 &comp->Installed );
1654 if (r == ERROR_SUCCESS) continue;
1656 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1657 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1658 &comp->Installed );
1659 if (r == ERROR_SUCCESS) continue;
1661 comp->Installed = INSTALLSTATE_ABSENT;
1665 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1667 MSIFEATURE *feature;
1669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1671 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1673 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1674 feature->Installed = INSTALLSTATE_ABSENT;
1675 else
1676 feature->Installed = state;
1680 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1682 return (feature->Level > 0 && feature->Level <= level);
1685 static BOOL process_state_property(MSIPACKAGE* package, int level,
1686 LPCWSTR property, INSTALLSTATE state)
1688 LPWSTR override;
1689 MSIFEATURE *feature;
1691 override = msi_dup_property( package->db, property );
1692 if (!override)
1693 return FALSE;
1695 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1697 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1698 continue;
1700 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1702 if (!strcmpiW( override, szAll ))
1704 if (feature->Installed != state)
1706 feature->Action = state;
1707 feature->ActionRequest = state;
1710 else
1712 LPWSTR ptr = override;
1713 LPWSTR ptr2 = strchrW(override,',');
1715 while (ptr)
1717 int len = ptr2 - ptr;
1719 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1720 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1722 if (feature->Installed != state)
1724 feature->Action = state;
1725 feature->ActionRequest = state;
1727 break;
1729 if (ptr2)
1731 ptr=ptr2+1;
1732 ptr2 = strchrW(ptr,',');
1734 else
1735 break;
1739 msi_free(override);
1740 return TRUE;
1743 static BOOL process_overrides( MSIPACKAGE *package, int level )
1745 static const WCHAR szAddLocal[] =
1746 {'A','D','D','L','O','C','A','L',0};
1747 static const WCHAR szAddSource[] =
1748 {'A','D','D','S','O','U','R','C','E',0};
1749 static const WCHAR szAdvertise[] =
1750 {'A','D','V','E','R','T','I','S','E',0};
1751 BOOL ret = FALSE;
1753 /* all these activation/deactivation things happen in order and things
1754 * later on the list override things earlier on the list.
1756 * 0 INSTALLLEVEL processing
1757 * 1 ADDLOCAL
1758 * 2 REMOVE
1759 * 3 ADDSOURCE
1760 * 4 ADDDEFAULT
1761 * 5 REINSTALL
1762 * 6 ADVERTISE
1763 * 7 COMPADDLOCAL
1764 * 8 COMPADDSOURCE
1765 * 9 FILEADDLOCAL
1766 * 10 FILEADDSOURCE
1767 * 11 FILEADDDEFAULT
1769 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1770 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1771 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1772 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1773 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1775 if (ret && !package->full_reinstall)
1776 msi_set_property( package->db, szPreselected, szOne, -1 );
1778 return ret;
1781 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1783 int level;
1784 MSICOMPONENT* component;
1785 MSIFEATURE *feature;
1787 TRACE("Checking Install Level\n");
1789 level = msi_get_property_int(package->db, szInstallLevel, 1);
1791 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1793 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1795 if (!is_feature_selected( feature, level )) continue;
1797 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1799 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1801 feature->Action = INSTALLSTATE_SOURCE;
1802 feature->ActionRequest = INSTALLSTATE_SOURCE;
1804 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1806 feature->Action = INSTALLSTATE_ADVERTISED;
1807 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1809 else
1811 feature->Action = INSTALLSTATE_LOCAL;
1812 feature->ActionRequest = INSTALLSTATE_LOCAL;
1816 /* disable child features of unselected parent or follow parent */
1817 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1819 FeatureList *fl;
1821 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1823 if (!is_feature_selected( feature, level ))
1825 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1826 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1828 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1830 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1831 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1832 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1833 fl->feature->Action = feature->Action;
1834 fl->feature->ActionRequest = feature->ActionRequest;
1839 else /* preselected */
1841 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1843 if (!is_feature_selected( feature, level )) continue;
1845 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1847 if (feature->Installed == INSTALLSTATE_ABSENT)
1849 feature->Action = INSTALLSTATE_UNKNOWN;
1850 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1852 else
1854 feature->Action = feature->Installed;
1855 feature->ActionRequest = feature->Installed;
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 FeatureList *fl;
1863 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1865 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1866 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1868 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1869 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1870 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1871 fl->feature->Action = feature->Action;
1872 fl->feature->ActionRequest = feature->ActionRequest;
1878 /* now we want to set component state based based on feature state */
1879 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1881 ComponentList *cl;
1883 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1884 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1885 feature->ActionRequest, feature->Action);
1887 if (!is_feature_selected( feature, level )) continue;
1889 /* features with components that have compressed files are made local */
1890 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1892 if (cl->component->ForceLocalState &&
1893 feature->ActionRequest == INSTALLSTATE_SOURCE)
1895 feature->Action = INSTALLSTATE_LOCAL;
1896 feature->ActionRequest = INSTALLSTATE_LOCAL;
1897 break;
1901 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1903 component = cl->component;
1905 switch (feature->ActionRequest)
1907 case INSTALLSTATE_ABSENT:
1908 component->anyAbsent = 1;
1909 break;
1910 case INSTALLSTATE_ADVERTISED:
1911 component->hasAdvertiseFeature = 1;
1912 break;
1913 case INSTALLSTATE_SOURCE:
1914 component->hasSourceFeature = 1;
1915 break;
1916 case INSTALLSTATE_LOCAL:
1917 component->hasLocalFeature = 1;
1918 break;
1919 case INSTALLSTATE_DEFAULT:
1920 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1921 component->hasAdvertiseFeature = 1;
1922 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1923 component->hasSourceFeature = 1;
1924 else
1925 component->hasLocalFeature = 1;
1926 break;
1927 default:
1928 break;
1933 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1935 /* check if it's local or source */
1936 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1937 (component->hasLocalFeature || component->hasSourceFeature))
1939 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1940 !component->ForceLocalState)
1942 component->Action = INSTALLSTATE_SOURCE;
1943 component->ActionRequest = INSTALLSTATE_SOURCE;
1945 else
1947 component->Action = INSTALLSTATE_LOCAL;
1948 component->ActionRequest = INSTALLSTATE_LOCAL;
1950 continue;
1953 /* if any feature is local, the component must be local too */
1954 if (component->hasLocalFeature)
1956 component->Action = INSTALLSTATE_LOCAL;
1957 component->ActionRequest = INSTALLSTATE_LOCAL;
1958 continue;
1960 if (component->hasSourceFeature)
1962 component->Action = INSTALLSTATE_SOURCE;
1963 component->ActionRequest = INSTALLSTATE_SOURCE;
1964 continue;
1966 if (component->hasAdvertiseFeature)
1968 component->Action = INSTALLSTATE_ADVERTISED;
1969 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1970 continue;
1972 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1973 if (component->anyAbsent && component->ComponentId)
1975 component->Action = INSTALLSTATE_ABSENT;
1976 component->ActionRequest = INSTALLSTATE_ABSENT;
1980 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1982 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1984 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1985 component->Action = INSTALLSTATE_LOCAL;
1986 component->ActionRequest = INSTALLSTATE_LOCAL;
1989 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1990 component->Installed == INSTALLSTATE_SOURCE &&
1991 component->hasSourceFeature)
1993 component->Action = INSTALLSTATE_UNKNOWN;
1994 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1997 TRACE("component %s (installed %d request %d action %d)\n",
1998 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2000 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
2001 component->num_clients++;
2002 else if (component->Action == INSTALLSTATE_ABSENT)
2003 component->num_clients--;
2006 return ERROR_SUCCESS;
2009 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2011 MSIPACKAGE *package = param;
2012 LPCWSTR name;
2013 MSIFEATURE *feature;
2015 name = MSI_RecordGetString( row, 1 );
2017 feature = msi_get_loaded_feature( package, name );
2018 if (!feature)
2019 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2020 else
2022 LPCWSTR Condition;
2023 Condition = MSI_RecordGetString(row,3);
2025 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2027 int level = MSI_RecordGetInteger(row,2);
2028 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2029 feature->Level = level;
2032 return ERROR_SUCCESS;
2035 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2037 static const WCHAR name[] = {'\\',0};
2038 VS_FIXEDFILEINFO *ptr, *ret;
2039 LPVOID version;
2040 DWORD versize, handle;
2041 UINT sz;
2043 versize = GetFileVersionInfoSizeW( filename, &handle );
2044 if (!versize)
2045 return NULL;
2047 version = msi_alloc( versize );
2048 if (!version)
2049 return NULL;
2051 GetFileVersionInfoW( filename, 0, versize, version );
2053 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2055 msi_free( version );
2056 return NULL;
2059 ret = msi_alloc( sz );
2060 memcpy( ret, ptr, sz );
2062 msi_free( version );
2063 return ret;
2066 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2068 DWORD ms, ls;
2070 msi_parse_version_string( version, &ms, &ls );
2072 if (fi->dwFileVersionMS > ms) return 1;
2073 else if (fi->dwFileVersionMS < ms) return -1;
2074 else if (fi->dwFileVersionLS > ls) return 1;
2075 else if (fi->dwFileVersionLS < ls) return -1;
2076 return 0;
2079 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2081 DWORD ms1, ms2;
2083 msi_parse_version_string( ver1, &ms1, NULL );
2084 msi_parse_version_string( ver2, &ms2, NULL );
2086 if (ms1 > ms2) return 1;
2087 else if (ms1 < ms2) return -1;
2088 return 0;
2091 DWORD msi_get_disk_file_size( LPCWSTR filename )
2093 HANDLE file;
2094 DWORD size;
2096 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2097 if (file == INVALID_HANDLE_VALUE)
2098 return INVALID_FILE_SIZE;
2100 size = GetFileSize( file, NULL );
2101 TRACE("size is %u\n", size);
2102 CloseHandle( file );
2103 return size;
2106 BOOL msi_file_hash_matches( MSIFILE *file )
2108 UINT r;
2109 MSIFILEHASHINFO hash;
2111 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2112 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2113 if (r != ERROR_SUCCESS)
2114 return FALSE;
2116 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2119 static WCHAR *get_temp_dir( void )
2121 static UINT id;
2122 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2124 GetTempPathW( MAX_PATH, tmp );
2125 for (;;)
2127 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2128 if (CreateDirectoryW( dir, NULL )) break;
2130 return strdupW( dir );
2134 * msi_build_directory_name()
2136 * This function is to save messing round with directory names
2137 * It handles adding backslashes between path segments,
2138 * and can add \ at the end of the directory name if told to.
2140 * It takes a variable number of arguments.
2141 * It always allocates a new string for the result, so make sure
2142 * to free the return value when finished with it.
2144 * The first arg is the number of path segments that follow.
2145 * The arguments following count are a list of path segments.
2146 * A path segment may be NULL.
2148 * Path segments will be added with a \ separating them.
2149 * A \ will not be added after the last segment, however if the
2150 * last segment is NULL, then the last character will be a \
2152 WCHAR *msi_build_directory_name( DWORD count, ... )
2154 DWORD sz = 1, i;
2155 WCHAR *dir;
2156 va_list va;
2158 va_start( va, count );
2159 for (i = 0; i < count; i++)
2161 const WCHAR *str = va_arg( va, const WCHAR * );
2162 if (str) sz += strlenW( str ) + 1;
2164 va_end( va );
2166 dir = msi_alloc( sz * sizeof(WCHAR) );
2167 dir[0] = 0;
2169 va_start( va, count );
2170 for (i = 0; i < count; i++)
2172 const WCHAR *str = va_arg( va, const WCHAR * );
2173 if (!str) continue;
2174 strcatW( dir, str );
2175 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2177 va_end( va );
2178 return dir;
2181 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2183 MSIASSEMBLY *assembly = file->Component->assembly;
2185 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2187 msi_free( file->TargetPath );
2188 if (assembly && !assembly->application)
2190 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2191 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2192 msi_track_tempfile( package, file->TargetPath );
2194 else
2196 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2197 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2200 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2203 static UINT calculate_file_cost( MSIPACKAGE *package )
2205 VS_FIXEDFILEINFO *file_version;
2206 WCHAR *font_version;
2207 MSIFILE *file;
2209 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2211 MSICOMPONENT *comp = file->Component;
2212 DWORD file_size;
2214 if (!comp->Enabled) continue;
2216 if (file->IsCompressed)
2217 comp->ForceLocalState = TRUE;
2219 set_target_path( package, file );
2221 if ((comp->assembly && !comp->assembly->installed) ||
2222 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2224 comp->Cost += file->FileSize;
2225 continue;
2227 file_size = msi_get_disk_file_size( file->TargetPath );
2229 if (file->Version)
2231 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2233 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2235 comp->Cost += file->FileSize - file_size;
2237 msi_free( file_version );
2238 continue;
2240 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2242 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2244 comp->Cost += file->FileSize - file_size;
2246 msi_free( font_version );
2247 continue;
2250 if (file_size != file->FileSize)
2252 comp->Cost += file->FileSize - file_size;
2255 return ERROR_SUCCESS;
2258 WCHAR *msi_normalize_path( const WCHAR *in )
2260 const WCHAR *p = in;
2261 WCHAR *q, *ret;
2262 int n, len = strlenW( in ) + 2;
2264 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2266 len = 0;
2267 while (1)
2269 /* copy until the end of the string or a space */
2270 while (*p != ' ' && (*q = *p))
2272 p++, len++;
2273 /* reduce many backslashes to one */
2274 if (*p != '\\' || *q != '\\')
2275 q++;
2278 /* quit at the end of the string */
2279 if (!*p)
2280 break;
2282 /* count the number of spaces */
2283 n = 0;
2284 while (p[n] == ' ')
2285 n++;
2287 /* if it's leading or trailing space, skip it */
2288 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2289 p += n;
2290 else /* copy n spaces */
2291 while (n && (*q++ = *p++)) n--;
2293 while (q - ret > 0 && q[-1] == ' ') q--;
2294 if (q - ret > 0 && q[-1] != '\\')
2296 q[0] = '\\';
2297 q[1] = 0;
2299 return ret;
2302 static WCHAR *get_install_location( MSIPACKAGE *package )
2304 HKEY hkey;
2305 WCHAR *path;
2307 if (!package->ProductCode) return NULL;
2308 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE ))
2309 return NULL;
2310 path = msi_reg_get_val_str( hkey, szInstallLocation );
2311 RegCloseKey( hkey );
2312 return path;
2315 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2317 FolderList *fl;
2318 MSIFOLDER *folder, *parent, *child;
2319 WCHAR *path, *normalized_path;
2321 TRACE("resolving %s\n", debugstr_w(name));
2323 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2325 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2327 if (!(path = get_install_location( package )) &&
2328 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2330 path = msi_dup_property( package->db, szRootDrive );
2333 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2335 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2337 parent = msi_get_loaded_folder( package, folder->Parent );
2338 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2340 else
2341 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2343 normalized_path = msi_normalize_path( path );
2344 msi_free( path );
2345 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2347 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2348 msi_free( normalized_path );
2349 return;
2351 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2352 msi_free( folder->ResolvedTarget );
2353 folder->ResolvedTarget = normalized_path;
2355 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2357 child = fl->folder;
2358 msi_resolve_target_folder( package, child->Directory, load_prop );
2360 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2363 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2365 static const WCHAR query[] = {
2366 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2367 '`','C','o','n','d','i','t','i','o','n','`',0};
2368 static const WCHAR szOutOfDiskSpace[] = {
2369 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2370 MSICOMPONENT *comp;
2371 MSIQUERY *view;
2372 LPWSTR level;
2373 UINT rc;
2375 TRACE("Building directory properties\n");
2376 msi_resolve_target_folder( package, szTargetDir, TRUE );
2378 TRACE("Evaluating component conditions\n");
2379 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2381 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2383 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2384 comp->Enabled = FALSE;
2386 else
2387 comp->Enabled = TRUE;
2389 get_client_counts( package );
2391 /* read components states from the registry */
2392 ACTION_GetComponentInstallStates(package);
2393 ACTION_GetFeatureInstallStates(package);
2395 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2397 TRACE("Evaluating feature conditions\n");
2399 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2400 if (rc == ERROR_SUCCESS)
2402 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2403 msiobj_release( &view->hdr );
2404 if (rc != ERROR_SUCCESS)
2405 return rc;
2409 TRACE("Calculating file cost\n");
2410 calculate_file_cost( package );
2412 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2413 /* set default run level if not set */
2414 level = msi_dup_property( package->db, szInstallLevel );
2415 if (!level)
2416 msi_set_property( package->db, szInstallLevel, szOne, -1 );
2417 msi_free(level);
2419 /* FIXME: check volume disk space */
2420 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2422 return MSI_SetFeatureStates(package);
2425 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size )
2427 BYTE *data = NULL;
2429 if (!value)
2431 *size = sizeof(WCHAR);
2432 *type = REG_SZ;
2433 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2434 return data;
2436 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2438 if (value[1]=='x')
2440 LPWSTR ptr;
2441 CHAR byte[5];
2442 LPWSTR deformated = NULL;
2443 int count;
2445 deformat_string(package, &value[2], &deformated);
2447 /* binary value type */
2448 ptr = deformated;
2449 *type = REG_BINARY;
2450 if (strlenW(ptr)%2)
2451 *size = (strlenW(ptr)/2)+1;
2452 else
2453 *size = strlenW(ptr)/2;
2455 data = msi_alloc(*size);
2457 byte[0] = '0';
2458 byte[1] = 'x';
2459 byte[4] = 0;
2460 count = 0;
2461 /* if uneven pad with a zero in front */
2462 if (strlenW(ptr)%2)
2464 byte[2]= '0';
2465 byte[3]= *ptr;
2466 ptr++;
2467 data[count] = (BYTE)strtol(byte,NULL,0);
2468 count ++;
2469 TRACE("Uneven byte count\n");
2471 while (*ptr)
2473 byte[2]= *ptr;
2474 ptr++;
2475 byte[3]= *ptr;
2476 ptr++;
2477 data[count] = (BYTE)strtol(byte,NULL,0);
2478 count ++;
2480 msi_free(deformated);
2482 TRACE("Data %i bytes(%i)\n",*size,count);
2484 else
2486 LPWSTR deformated;
2487 LPWSTR p;
2488 DWORD d = 0;
2489 deformat_string(package, &value[1], &deformated);
2491 *type=REG_DWORD;
2492 *size = sizeof(DWORD);
2493 data = msi_alloc(*size);
2494 p = deformated;
2495 if (*p == '-')
2496 p++;
2497 while (*p)
2499 if ( (*p < '0') || (*p > '9') )
2500 break;
2501 d *= 10;
2502 d += (*p - '0');
2503 p++;
2505 if (deformated[0] == '-')
2506 d = -d;
2507 *(LPDWORD)data = d;
2508 TRACE("DWORD %i\n",*(LPDWORD)data);
2510 msi_free(deformated);
2513 else
2515 const WCHAR *ptr = value;
2516 DWORD len;
2518 *type = REG_SZ;
2519 if (value[0] == '#')
2521 ptr++;
2522 if (value[1] == '%')
2524 ptr++;
2525 *type = REG_EXPAND_SZ;
2528 len = deformat_string( package, ptr, (WCHAR **)&data );
2529 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2530 *size = (len + 1) * sizeof(WCHAR);
2532 return data;
2535 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2537 const WCHAR *ret;
2539 switch (root)
2541 case -1:
2542 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2544 *root_key = HKEY_LOCAL_MACHINE;
2545 ret = szHLM;
2547 else
2549 *root_key = HKEY_CURRENT_USER;
2550 ret = szHCU;
2552 break;
2553 case 0:
2554 *root_key = HKEY_CLASSES_ROOT;
2555 ret = szHCR;
2556 break;
2557 case 1:
2558 *root_key = HKEY_CURRENT_USER;
2559 ret = szHCU;
2560 break;
2561 case 2:
2562 *root_key = HKEY_LOCAL_MACHINE;
2563 ret = szHLM;
2564 break;
2565 case 3:
2566 *root_key = HKEY_USERS;
2567 ret = szHU;
2568 break;
2569 default:
2570 ERR("Unknown root %i\n", root);
2571 return NULL;
2574 return ret;
2577 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2579 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2580 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2582 if ((is_64bit || is_wow64) &&
2583 !(comp->Attributes & msidbComponentAttributes64bit) &&
2584 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2586 UINT size;
2587 WCHAR *path_32node;
2589 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2590 if (!(path_32node = msi_alloc( size ))) return NULL;
2592 memcpy( path_32node, path, len * sizeof(WCHAR) );
2593 strcpyW( path_32node + len, szWow6432Node );
2594 strcatW( path_32node, szBackSlash );
2595 strcatW( path_32node, path + len );
2596 return path_32node;
2598 return strdupW( path );
2601 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2603 REGSAM access = KEY_ALL_ACCESS;
2604 WCHAR *subkey, *p, *q;
2605 HKEY hkey, ret = NULL;
2606 LONG res;
2608 if (is_wow64) access |= KEY_WOW64_64KEY;
2610 if (!(subkey = strdupW( path ))) return NULL;
2611 p = subkey;
2612 if ((q = strchrW( p, '\\' ))) *q = 0;
2613 if (create)
2614 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2615 else
2616 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2617 if (res)
2619 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2620 msi_free( subkey );
2621 return NULL;
2623 if (q && q[1])
2625 ret = open_key( hkey, q + 1, create );
2626 RegCloseKey( hkey );
2628 else ret = hkey;
2629 msi_free( subkey );
2630 return ret;
2633 static BOOL is_special_entry( const WCHAR *name )
2635 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2638 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2640 const WCHAR *p = str;
2641 WCHAR **ret;
2642 int i = 0;
2644 *count = 0;
2645 if (!str) return NULL;
2646 while ((p - str) < len)
2648 p += strlenW( p ) + 1;
2649 (*count)++;
2651 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2652 p = str;
2653 while ((p - str) < len)
2655 if (!(ret[i] = strdupW( p )))
2657 for (; i >= 0; i--) msi_free( ret[i] );
2658 msi_free( ret );
2659 return NULL;
2661 p += strlenW( p ) + 1;
2662 i++;
2664 return ret;
2667 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2668 WCHAR **right, DWORD right_count, DWORD *size )
2670 WCHAR *ret, *p;
2671 unsigned int i;
2673 *size = sizeof(WCHAR);
2674 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2675 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2677 if (!(ret = p = msi_alloc( *size ))) return NULL;
2679 for (i = 0; i < left_count; i++)
2681 strcpyW( p, left[i] );
2682 p += strlenW( p ) + 1;
2684 for (i = 0; i < right_count; i++)
2686 strcpyW( p, right[i] );
2687 p += strlenW( p ) + 1;
2689 *p = 0;
2690 return ret;
2693 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2694 WCHAR **new, DWORD new_count )
2696 DWORD ret = old_count;
2697 unsigned int i, j, k;
2699 for (i = 0; i < new_count; i++)
2701 for (j = 0; j < old_count; j++)
2703 if (old[j] && !strcmpW( new[i], old[j] ))
2705 msi_free( old[j] );
2706 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2707 old[k] = NULL;
2708 ret--;
2712 return ret;
2715 enum join_op
2717 JOIN_OP_APPEND,
2718 JOIN_OP_PREPEND,
2719 JOIN_OP_REPLACE
2722 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2723 WCHAR **new, DWORD new_count, DWORD *size )
2725 switch (op)
2727 case JOIN_OP_APPEND:
2728 old_count = remove_duplicate_values( old, old_count, new, new_count );
2729 return flatten_multi_string_values( old, old_count, new, new_count, size );
2731 case JOIN_OP_PREPEND:
2732 old_count = remove_duplicate_values( old, old_count, new, new_count );
2733 return flatten_multi_string_values( new, new_count, old, old_count, size );
2735 case JOIN_OP_REPLACE:
2736 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2738 default:
2739 ERR("unhandled join op %u\n", op);
2740 return NULL;
2744 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2745 BYTE *new_value, DWORD new_size, DWORD *size )
2747 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2748 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2749 enum join_op op = JOIN_OP_REPLACE;
2750 WCHAR **old = NULL, **new = NULL;
2751 BYTE *ret;
2753 if (new_size / sizeof(WCHAR) - 1 > 1)
2755 new_ptr = (const WCHAR *)new_value;
2756 new_len = new_size / sizeof(WCHAR) - 1;
2758 if (!new_ptr[0] && new_ptr[new_len - 1])
2760 op = JOIN_OP_APPEND;
2761 new_len--;
2762 new_ptr++;
2764 else if (new_ptr[0] && !new_ptr[new_len - 1])
2766 op = JOIN_OP_PREPEND;
2767 new_len--;
2769 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2771 op = JOIN_OP_REPLACE;
2772 new_len -= 2;
2773 new_ptr++;
2775 new = split_multi_string_values( new_ptr, new_len, &new_count );
2777 if (old_size / sizeof(WCHAR) - 1 > 1)
2779 old_ptr = (const WCHAR *)old_value;
2780 old_len = old_size / sizeof(WCHAR) - 1;
2781 old = split_multi_string_values( old_ptr, old_len, &old_count );
2783 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2784 for (i = 0; i < old_count; i++) msi_free( old[i] );
2785 for (i = 0; i < new_count; i++) msi_free( new[i] );
2786 msi_free( old );
2787 msi_free( new );
2788 return ret;
2791 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2793 BYTE *ret;
2794 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2795 if (!(ret = msi_alloc( *size ))) return NULL;
2796 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2797 return ret;
2800 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2802 MSIPACKAGE *package = param;
2803 BYTE *new_value, *old_value = NULL;
2804 HKEY root_key, hkey;
2805 DWORD type, old_type, new_size, old_size = 0;
2806 LPWSTR deformated, uikey, keypath;
2807 const WCHAR *szRoot, *component, *name, *key, *str;
2808 MSICOMPONENT *comp;
2809 MSIRECORD * uirow;
2810 INT root;
2811 BOOL check_first = FALSE;
2812 int len;
2814 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2816 component = MSI_RecordGetString(row, 6);
2817 comp = msi_get_loaded_component(package,component);
2818 if (!comp)
2819 return ERROR_SUCCESS;
2821 comp->Action = msi_get_component_action( package, comp );
2822 if (comp->Action != INSTALLSTATE_LOCAL)
2824 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2825 return ERROR_SUCCESS;
2828 name = MSI_RecordGetString(row, 4);
2829 if( MSI_RecordIsNull(row,5) && name )
2831 /* null values can have special meanings */
2832 if (name[0]=='-' && name[1] == 0)
2833 return ERROR_SUCCESS;
2834 if ((name[0] == '+' || name[0] == '*') && !name[1])
2835 check_first = TRUE;
2838 root = MSI_RecordGetInteger(row,2);
2839 key = MSI_RecordGetString(row, 3);
2841 szRoot = get_root_key( package, root, &root_key );
2842 if (!szRoot)
2843 return ERROR_SUCCESS;
2845 deformat_string(package, key , &deformated);
2846 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2847 strcpyW(uikey,szRoot);
2848 strcatW(uikey,deformated);
2850 keypath = get_keypath( comp, root_key, deformated );
2851 msi_free( deformated );
2852 if (!(hkey = open_key( root_key, keypath, TRUE )))
2854 ERR("Could not create key %s\n", debugstr_w(keypath));
2855 msi_free(uikey);
2856 msi_free(keypath);
2857 return ERROR_FUNCTION_FAILED;
2859 str = msi_record_get_string( row, 5, &len );
2860 if (str && len > strlenW( str ))
2862 type = REG_MULTI_SZ;
2863 new_size = (len + 1) * sizeof(WCHAR);
2864 new_value = (BYTE *)msi_strdupW( str, len );
2866 else new_value = parse_value( package, str, &type, &new_size );
2867 deformat_string(package, name, &deformated);
2869 if (!is_special_entry( name ))
2871 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2872 if (type == REG_MULTI_SZ)
2874 BYTE *new;
2875 if (old_value && old_type != REG_MULTI_SZ)
2877 msi_free( old_value );
2878 old_value = NULL;
2879 old_size = 0;
2881 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2882 msi_free( new_value );
2883 new_value = new;
2885 if (!check_first)
2887 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2888 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2890 else if (!old_value)
2892 if (deformated || new_size)
2894 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2895 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2898 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2900 RegCloseKey(hkey);
2902 uirow = MSI_CreateRecord(3);
2903 MSI_RecordSetStringW(uirow,2,deformated);
2904 MSI_RecordSetStringW(uirow,1,uikey);
2905 if (type == REG_SZ || type == REG_EXPAND_SZ)
2906 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2907 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2908 msiobj_release( &uirow->hdr );
2910 msi_free(new_value);
2911 msi_free(old_value);
2912 msi_free(deformated);
2913 msi_free(uikey);
2914 msi_free(keypath);
2916 return ERROR_SUCCESS;
2919 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2921 static const WCHAR query[] = {
2922 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2923 '`','R','e','g','i','s','t','r','y','`',0};
2924 MSIQUERY *view;
2925 UINT rc;
2927 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2928 if (rc != ERROR_SUCCESS)
2929 return ERROR_SUCCESS;
2931 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2932 msiobj_release(&view->hdr);
2933 return rc;
2936 static void delete_key( HKEY root, const WCHAR *path )
2938 REGSAM access = 0;
2939 WCHAR *subkey, *p;
2940 HKEY hkey;
2941 LONG res;
2943 if (is_wow64) access |= KEY_WOW64_64KEY;
2945 if (!(subkey = strdupW( path ))) return;
2946 for (;;)
2948 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2949 hkey = open_key( root, subkey, FALSE );
2950 if (!hkey) break;
2951 if (p && p[1])
2952 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2953 else
2954 res = RegDeleteKeyExW( root, subkey, access, 0 );
2955 if (res)
2957 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2958 break;
2960 if (p && p[1]) RegCloseKey( hkey );
2961 else break;
2963 msi_free( subkey );
2966 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2968 LONG res;
2969 HKEY hkey;
2970 DWORD num_subkeys, num_values;
2972 if ((hkey = open_key( root, path, FALSE )))
2974 if ((res = RegDeleteValueW( hkey, value )))
2975 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2977 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2978 NULL, NULL, NULL, NULL );
2979 RegCloseKey( hkey );
2980 if (!res && !num_subkeys && !num_values)
2982 TRACE("removing empty key %s\n", debugstr_w(path));
2983 delete_key( root, path );
2988 static void delete_tree( HKEY root, const WCHAR *path )
2990 LONG res;
2991 HKEY hkey;
2993 if (!(hkey = open_key( root, path, FALSE ))) return;
2994 res = RegDeleteTreeW( hkey, NULL );
2995 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
2996 delete_key( root, path );
2997 RegCloseKey( hkey );
3000 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3002 MSIPACKAGE *package = param;
3003 LPCWSTR component, name, key_str, root_key_str;
3004 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3005 MSICOMPONENT *comp;
3006 MSIRECORD *uirow;
3007 BOOL delete_key = FALSE;
3008 HKEY hkey_root;
3009 UINT size;
3010 INT root;
3012 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3014 component = MSI_RecordGetString( row, 6 );
3015 comp = msi_get_loaded_component( package, component );
3016 if (!comp)
3017 return ERROR_SUCCESS;
3019 comp->Action = msi_get_component_action( package, comp );
3020 if (comp->Action != INSTALLSTATE_ABSENT)
3022 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3023 return ERROR_SUCCESS;
3026 name = MSI_RecordGetString( row, 4 );
3027 if (MSI_RecordIsNull( row, 5 ) && name )
3029 if (name[0] == '+' && !name[1])
3030 return ERROR_SUCCESS;
3031 if ((name[0] == '-' || name[0] == '*') && !name[1])
3033 delete_key = TRUE;
3034 name = NULL;
3038 root = MSI_RecordGetInteger( row, 2 );
3039 key_str = MSI_RecordGetString( row, 3 );
3041 root_key_str = get_root_key( package, root, &hkey_root );
3042 if (!root_key_str)
3043 return ERROR_SUCCESS;
3045 deformat_string( package, key_str, &deformated_key );
3046 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3047 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3048 strcpyW( ui_key_str, root_key_str );
3049 strcatW( ui_key_str, deformated_key );
3051 deformat_string( package, name, &deformated_name );
3053 keypath = get_keypath( comp, hkey_root, deformated_key );
3054 msi_free( deformated_key );
3055 if (delete_key) delete_tree( hkey_root, keypath );
3056 else delete_value( hkey_root, keypath, deformated_name );
3057 msi_free( keypath );
3059 uirow = MSI_CreateRecord( 2 );
3060 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3061 MSI_RecordSetStringW( uirow, 2, deformated_name );
3062 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3063 msiobj_release( &uirow->hdr );
3065 msi_free( ui_key_str );
3066 msi_free( deformated_name );
3067 return ERROR_SUCCESS;
3070 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3072 MSIPACKAGE *package = param;
3073 LPCWSTR component, name, key_str, root_key_str;
3074 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3075 MSICOMPONENT *comp;
3076 MSIRECORD *uirow;
3077 BOOL delete_key = FALSE;
3078 HKEY hkey_root;
3079 UINT size;
3080 INT root;
3082 component = MSI_RecordGetString( row, 5 );
3083 comp = msi_get_loaded_component( package, component );
3084 if (!comp)
3085 return ERROR_SUCCESS;
3087 comp->Action = msi_get_component_action( package, comp );
3088 if (comp->Action != INSTALLSTATE_LOCAL)
3090 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3091 return ERROR_SUCCESS;
3094 if ((name = MSI_RecordGetString( row, 4 )))
3096 if (name[0] == '-' && !name[1])
3098 delete_key = TRUE;
3099 name = NULL;
3103 root = MSI_RecordGetInteger( row, 2 );
3104 key_str = MSI_RecordGetString( row, 3 );
3106 root_key_str = get_root_key( package, root, &hkey_root );
3107 if (!root_key_str)
3108 return ERROR_SUCCESS;
3110 deformat_string( package, key_str, &deformated_key );
3111 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3112 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3113 strcpyW( ui_key_str, root_key_str );
3114 strcatW( ui_key_str, deformated_key );
3116 deformat_string( package, name, &deformated_name );
3118 keypath = get_keypath( comp, hkey_root, deformated_key );
3119 msi_free( deformated_key );
3120 if (delete_key) delete_tree( hkey_root, keypath );
3121 else delete_value( hkey_root, keypath, deformated_name );
3122 msi_free( keypath );
3124 uirow = MSI_CreateRecord( 2 );
3125 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3126 MSI_RecordSetStringW( uirow, 2, deformated_name );
3127 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3128 msiobj_release( &uirow->hdr );
3130 msi_free( ui_key_str );
3131 msi_free( deformated_name );
3132 return ERROR_SUCCESS;
3135 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3137 static const WCHAR registry_query[] = {
3138 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3139 '`','R','e','g','i','s','t','r','y','`',0};
3140 static const WCHAR remove_registry_query[] = {
3141 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3142 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3143 MSIQUERY *view;
3144 UINT rc;
3146 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3147 if (rc == ERROR_SUCCESS)
3149 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3150 msiobj_release( &view->hdr );
3151 if (rc != ERROR_SUCCESS)
3152 return rc;
3154 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3155 if (rc == ERROR_SUCCESS)
3157 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3158 msiobj_release( &view->hdr );
3159 if (rc != ERROR_SUCCESS)
3160 return rc;
3162 return ERROR_SUCCESS;
3165 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3167 package->script->CurrentlyScripting = TRUE;
3169 return ERROR_SUCCESS;
3173 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3175 static const WCHAR query[]= {
3176 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3177 '`','R','e','g','i','s','t','r','y','`',0};
3178 MSICOMPONENT *comp;
3179 DWORD total = 0, count = 0;
3180 MSIQUERY *view;
3181 MSIFEATURE *feature;
3182 MSIFILE *file;
3183 UINT rc;
3185 TRACE("InstallValidate\n");
3187 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3188 if (rc == ERROR_SUCCESS)
3190 rc = MSI_IterateRecords( view, &count, NULL, package );
3191 msiobj_release( &view->hdr );
3192 if (rc != ERROR_SUCCESS)
3193 return rc;
3194 total += count * REG_PROGRESS_VALUE;
3196 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3197 total += COMPONENT_PROGRESS_VALUE;
3199 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3200 total += file->FileSize;
3202 msi_ui_progress( package, 0, total, 0, 0 );
3204 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3206 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3207 debugstr_w(feature->Feature), feature->Installed,
3208 feature->ActionRequest, feature->Action);
3210 return ERROR_SUCCESS;
3213 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3215 MSIPACKAGE* package = param;
3216 LPCWSTR cond = NULL;
3217 LPCWSTR message = NULL;
3218 UINT r;
3220 static const WCHAR title[]=
3221 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3223 cond = MSI_RecordGetString(row,1);
3225 r = MSI_EvaluateConditionW(package,cond);
3226 if (r == MSICONDITION_FALSE)
3228 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3230 LPWSTR deformated;
3231 message = MSI_RecordGetString(row,2);
3232 deformat_string(package,message,&deformated);
3233 MessageBoxW(NULL,deformated,title,MB_OK);
3234 msi_free(deformated);
3237 return ERROR_INSTALL_FAILURE;
3240 return ERROR_SUCCESS;
3243 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3245 static const WCHAR query[] = {
3246 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3247 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3248 MSIQUERY *view;
3249 UINT rc;
3251 TRACE("Checking launch conditions\n");
3253 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3254 if (rc != ERROR_SUCCESS)
3255 return ERROR_SUCCESS;
3257 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3258 msiobj_release(&view->hdr);
3259 return rc;
3262 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3265 if (!cmp->KeyPath)
3266 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3268 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3270 static const WCHAR query[] = {
3271 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3272 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3273 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3274 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3275 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3276 MSIRECORD *row;
3277 UINT root, len;
3278 LPWSTR deformated, buffer, deformated_name;
3279 LPCWSTR key, name;
3281 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3282 if (!row)
3283 return NULL;
3285 root = MSI_RecordGetInteger(row,2);
3286 key = MSI_RecordGetString(row, 3);
3287 name = MSI_RecordGetString(row, 4);
3288 deformat_string(package, key , &deformated);
3289 deformat_string(package, name, &deformated_name);
3291 len = strlenW(deformated) + 6;
3292 if (deformated_name)
3293 len+=strlenW(deformated_name);
3295 buffer = msi_alloc( len *sizeof(WCHAR));
3297 if (deformated_name)
3298 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3299 else
3300 sprintfW(buffer,fmt,root,deformated);
3302 msi_free(deformated);
3303 msi_free(deformated_name);
3304 msiobj_release(&row->hdr);
3306 return buffer;
3308 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3310 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3311 return NULL;
3313 else
3315 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3317 if (file)
3318 return strdupW( file->TargetPath );
3320 return NULL;
3323 static HKEY openSharedDLLsKey(void)
3325 HKEY hkey=0;
3326 static const WCHAR path[] =
3327 {'S','o','f','t','w','a','r','e','\\',
3328 'M','i','c','r','o','s','o','f','t','\\',
3329 'W','i','n','d','o','w','s','\\',
3330 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3331 'S','h','a','r','e','d','D','L','L','s',0};
3333 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3334 return hkey;
3337 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3339 HKEY hkey;
3340 DWORD count=0;
3341 DWORD type;
3342 DWORD sz = sizeof(count);
3343 DWORD rc;
3345 hkey = openSharedDLLsKey();
3346 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3347 if (rc != ERROR_SUCCESS)
3348 count = 0;
3349 RegCloseKey(hkey);
3350 return count;
3353 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3355 HKEY hkey;
3357 hkey = openSharedDLLsKey();
3358 if (count > 0)
3359 msi_reg_set_val_dword( hkey, path, count );
3360 else
3361 RegDeleteValueW(hkey,path);
3362 RegCloseKey(hkey);
3363 return count;
3366 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3368 MSIFEATURE *feature;
3369 INT count = 0;
3370 BOOL write = FALSE;
3372 /* only refcount DLLs */
3373 if (comp->KeyPath == NULL ||
3374 comp->assembly ||
3375 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3376 comp->Attributes & msidbComponentAttributesODBCDataSource)
3377 write = FALSE;
3378 else
3380 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3381 write = (count > 0);
3383 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3384 write = TRUE;
3387 /* increment counts */
3388 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3390 ComponentList *cl;
3392 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3393 continue;
3395 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3397 if ( cl->component == comp )
3398 count++;
3402 /* decrement counts */
3403 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3405 ComponentList *cl;
3407 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3408 continue;
3410 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3412 if ( cl->component == comp )
3413 count--;
3417 /* ref count all the files in the component */
3418 if (write)
3420 MSIFILE *file;
3422 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3424 if (file->Component == comp)
3425 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3429 /* add a count for permanent */
3430 if (comp->Attributes & msidbComponentAttributesPermanent)
3431 count ++;
3433 comp->RefCount = count;
3435 if (write)
3436 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3439 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3441 if (comp->assembly)
3443 const WCHAR prefixW[] = {'<','\\',0};
3444 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3445 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3447 if (keypath)
3449 strcpyW( keypath, prefixW );
3450 strcatW( keypath, comp->assembly->display_name );
3452 return keypath;
3454 return resolve_keypath( package, comp );
3457 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3459 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3460 UINT rc;
3461 MSICOMPONENT *comp;
3462 HKEY hkey;
3464 TRACE("\n");
3466 squash_guid(package->ProductCode,squished_pc);
3467 msi_set_sourcedir_props(package, FALSE);
3469 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3471 MSIRECORD *uirow;
3472 INSTALLSTATE action;
3474 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3475 if (!comp->ComponentId)
3476 continue;
3478 squash_guid( comp->ComponentId, squished_cc );
3479 msi_free( comp->FullKeypath );
3480 comp->FullKeypath = build_full_keypath( package, comp );
3482 ACTION_RefCountComponent( package, comp );
3484 if (package->need_rollback) action = comp->Installed;
3485 else action = comp->ActionRequest;
3487 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3488 debugstr_w(comp->Component), debugstr_w(squished_cc),
3489 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3491 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3493 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3494 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3495 else
3496 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3498 if (rc != ERROR_SUCCESS)
3499 continue;
3501 if (comp->Attributes & msidbComponentAttributesPermanent)
3503 static const WCHAR szPermKey[] =
3504 { '0','0','0','0','0','0','0','0','0','0','0','0',
3505 '0','0','0','0','0','0','0','0','0','0','0','0',
3506 '0','0','0','0','0','0','0','0',0 };
3508 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3510 if (action == INSTALLSTATE_LOCAL)
3511 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3512 else
3514 MSIFILE *file;
3515 MSIRECORD *row;
3516 LPWSTR ptr, ptr2;
3517 WCHAR source[MAX_PATH];
3518 WCHAR base[MAX_PATH];
3519 LPWSTR sourcepath;
3521 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3522 static const WCHAR query[] = {
3523 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3524 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3525 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3526 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3527 '`','D','i','s','k','I','d','`',0};
3529 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3530 continue;
3532 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3533 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3534 ptr2 = strrchrW(source, '\\') + 1;
3535 msiobj_release(&row->hdr);
3537 lstrcpyW(base, package->PackagePath);
3538 ptr = strrchrW(base, '\\');
3539 *(ptr + 1) = '\0';
3541 sourcepath = msi_resolve_file_source(package, file);
3542 ptr = sourcepath + lstrlenW(base);
3543 lstrcpyW(ptr2, ptr);
3544 msi_free(sourcepath);
3546 msi_reg_set_val_str(hkey, squished_pc, source);
3548 RegCloseKey(hkey);
3550 else if (action == INSTALLSTATE_ABSENT)
3552 if (comp->num_clients <= 0)
3554 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3555 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3556 else
3557 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3561 /* UI stuff */
3562 uirow = MSI_CreateRecord(3);
3563 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3564 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3565 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3566 msi_ui_actiondata( package, szProcessComponents, uirow );
3567 msiobj_release( &uirow->hdr );
3569 return ERROR_SUCCESS;
3572 typedef struct {
3573 CLSID clsid;
3574 LPWSTR source;
3576 LPWSTR path;
3577 ITypeLib *ptLib;
3578 } typelib_struct;
3580 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3581 LPWSTR lpszName, LONG_PTR lParam)
3583 TLIBATTR *attr;
3584 typelib_struct *tl_struct = (typelib_struct*) lParam;
3585 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3586 int sz;
3587 HRESULT res;
3589 if (!IS_INTRESOURCE(lpszName))
3591 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3592 return TRUE;
3595 sz = strlenW(tl_struct->source)+4;
3596 sz *= sizeof(WCHAR);
3598 if ((INT_PTR)lpszName == 1)
3599 tl_struct->path = strdupW(tl_struct->source);
3600 else
3602 tl_struct->path = msi_alloc(sz);
3603 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3606 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3607 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3608 if (FAILED(res))
3610 msi_free(tl_struct->path);
3611 tl_struct->path = NULL;
3613 return TRUE;
3616 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3617 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3619 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3620 return FALSE;
3623 msi_free(tl_struct->path);
3624 tl_struct->path = NULL;
3626 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3627 ITypeLib_Release(tl_struct->ptLib);
3629 return TRUE;
3632 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3634 MSIPACKAGE* package = param;
3635 LPCWSTR component;
3636 MSICOMPONENT *comp;
3637 MSIFILE *file;
3638 typelib_struct tl_struct;
3639 ITypeLib *tlib;
3640 HMODULE module;
3641 HRESULT hr;
3643 component = MSI_RecordGetString(row,3);
3644 comp = msi_get_loaded_component(package,component);
3645 if (!comp)
3646 return ERROR_SUCCESS;
3648 comp->Action = msi_get_component_action( package, comp );
3649 if (comp->Action != INSTALLSTATE_LOCAL)
3651 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3652 return ERROR_SUCCESS;
3655 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3657 TRACE("component has no key path\n");
3658 return ERROR_SUCCESS;
3660 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3662 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3663 if (module)
3665 LPCWSTR guid;
3666 guid = MSI_RecordGetString(row,1);
3667 CLSIDFromString( guid, &tl_struct.clsid);
3668 tl_struct.source = strdupW( file->TargetPath );
3669 tl_struct.path = NULL;
3671 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3672 (LONG_PTR)&tl_struct);
3674 if (tl_struct.path)
3676 LPCWSTR helpid, help_path = NULL;
3677 HRESULT res;
3679 helpid = MSI_RecordGetString(row,6);
3681 if (helpid) help_path = msi_get_target_folder( package, helpid );
3682 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3684 if (FAILED(res))
3685 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3686 else
3687 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3689 ITypeLib_Release(tl_struct.ptLib);
3690 msi_free(tl_struct.path);
3692 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3694 FreeLibrary(module);
3695 msi_free(tl_struct.source);
3697 else
3699 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3700 if (FAILED(hr))
3702 ERR("Failed to load type library: %08x\n", hr);
3703 return ERROR_INSTALL_FAILURE;
3706 ITypeLib_Release(tlib);
3709 return ERROR_SUCCESS;
3712 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3714 static const WCHAR query[] = {
3715 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3716 '`','T','y','p','e','L','i','b','`',0};
3717 MSIQUERY *view;
3718 UINT rc;
3720 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3721 if (rc != ERROR_SUCCESS)
3722 return ERROR_SUCCESS;
3724 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3725 msiobj_release(&view->hdr);
3726 return rc;
3729 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3731 MSIPACKAGE *package = param;
3732 LPCWSTR component, guid;
3733 MSICOMPONENT *comp;
3734 GUID libid;
3735 UINT version;
3736 LCID language;
3737 SYSKIND syskind;
3738 HRESULT hr;
3740 component = MSI_RecordGetString( row, 3 );
3741 comp = msi_get_loaded_component( package, component );
3742 if (!comp)
3743 return ERROR_SUCCESS;
3745 comp->Action = msi_get_component_action( package, comp );
3746 if (comp->Action != INSTALLSTATE_ABSENT)
3748 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3749 return ERROR_SUCCESS;
3751 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3753 guid = MSI_RecordGetString( row, 1 );
3754 CLSIDFromString( guid, &libid );
3755 version = MSI_RecordGetInteger( row, 4 );
3756 language = MSI_RecordGetInteger( row, 2 );
3758 #ifdef _WIN64
3759 syskind = SYS_WIN64;
3760 #else
3761 syskind = SYS_WIN32;
3762 #endif
3764 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3765 if (FAILED(hr))
3767 WARN("Failed to unregister typelib: %08x\n", hr);
3770 return ERROR_SUCCESS;
3773 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3775 static const WCHAR query[] = {
3776 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3777 '`','T','y','p','e','L','i','b','`',0};
3778 MSIQUERY *view;
3779 UINT rc;
3781 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3782 if (rc != ERROR_SUCCESS)
3783 return ERROR_SUCCESS;
3785 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3786 msiobj_release( &view->hdr );
3787 return rc;
3790 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3792 static const WCHAR szlnk[] = {'.','l','n','k',0};
3793 LPCWSTR directory, extension, link_folder;
3794 LPWSTR link_file, filename;
3796 directory = MSI_RecordGetString( row, 2 );
3797 link_folder = msi_get_target_folder( package, directory );
3798 if (!link_folder)
3800 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3801 return NULL;
3803 /* may be needed because of a bug somewhere else */
3804 msi_create_full_path( link_folder );
3806 filename = msi_dup_record_field( row, 3 );
3807 msi_reduce_to_long_filename( filename );
3809 extension = strchrW( filename, '.' );
3810 if (!extension || strcmpiW( extension, szlnk ))
3812 int len = strlenW( filename );
3813 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3814 memcpy( filename + len, szlnk, sizeof(szlnk) );
3816 link_file = msi_build_directory_name( 2, link_folder, filename );
3817 msi_free( filename );
3819 return link_file;
3822 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3824 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3825 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3826 WCHAR *folder, *dest, *path;
3828 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3829 folder = msi_dup_property( package->db, szWindowsFolder );
3830 else
3832 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3833 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3834 msi_free( appdata );
3836 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3837 msi_create_full_path( dest );
3838 path = msi_build_directory_name( 2, dest, icon_name );
3839 msi_free( folder );
3840 msi_free( dest );
3841 return path;
3844 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3846 MSIPACKAGE *package = param;
3847 LPWSTR link_file, deformated, path;
3848 LPCWSTR component, target;
3849 MSICOMPONENT *comp;
3850 IShellLinkW *sl = NULL;
3851 IPersistFile *pf = NULL;
3852 HRESULT res;
3854 component = MSI_RecordGetString(row, 4);
3855 comp = msi_get_loaded_component(package, component);
3856 if (!comp)
3857 return ERROR_SUCCESS;
3859 comp->Action = msi_get_component_action( package, comp );
3860 if (comp->Action != INSTALLSTATE_LOCAL)
3862 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3863 return ERROR_SUCCESS;
3865 msi_ui_actiondata( package, szCreateShortcuts, row );
3867 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3868 &IID_IShellLinkW, (LPVOID *) &sl );
3870 if (FAILED( res ))
3872 ERR("CLSID_ShellLink not available\n");
3873 goto err;
3876 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3877 if (FAILED( res ))
3879 ERR("QueryInterface(IID_IPersistFile) failed\n");
3880 goto err;
3883 target = MSI_RecordGetString(row, 5);
3884 if (strchrW(target, '['))
3886 deformat_string( package, target, &path );
3887 TRACE("target path is %s\n", debugstr_w(path));
3888 IShellLinkW_SetPath( sl, path );
3889 msi_free( path );
3891 else
3893 FIXME("poorly handled shortcut format, advertised shortcut\n");
3894 IShellLinkW_SetPath(sl,comp->FullKeypath);
3897 if (!MSI_RecordIsNull(row,6))
3899 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3900 deformat_string(package, arguments, &deformated);
3901 IShellLinkW_SetArguments(sl,deformated);
3902 msi_free(deformated);
3905 if (!MSI_RecordIsNull(row,7))
3907 LPCWSTR description = MSI_RecordGetString(row, 7);
3908 IShellLinkW_SetDescription(sl, description);
3911 if (!MSI_RecordIsNull(row,8))
3912 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3914 if (!MSI_RecordIsNull(row,9))
3916 INT index;
3917 LPCWSTR icon = MSI_RecordGetString(row, 9);
3919 path = msi_build_icon_path(package, icon);
3920 index = MSI_RecordGetInteger(row,10);
3922 /* no value means 0 */
3923 if (index == MSI_NULL_INTEGER)
3924 index = 0;
3926 IShellLinkW_SetIconLocation(sl, path, index);
3927 msi_free(path);
3930 if (!MSI_RecordIsNull(row,11))
3931 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3933 if (!MSI_RecordIsNull(row,12))
3935 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3936 full_path = msi_get_target_folder( package, wkdir );
3937 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3939 link_file = get_link_file(package, row);
3941 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3942 IPersistFile_Save(pf, link_file, FALSE);
3943 msi_free(link_file);
3945 err:
3946 if (pf)
3947 IPersistFile_Release( pf );
3948 if (sl)
3949 IShellLinkW_Release( sl );
3951 return ERROR_SUCCESS;
3954 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3956 static const WCHAR query[] = {
3957 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3958 '`','S','h','o','r','t','c','u','t','`',0};
3959 MSIQUERY *view;
3960 HRESULT res;
3961 UINT rc;
3963 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3964 if (rc != ERROR_SUCCESS)
3965 return ERROR_SUCCESS;
3967 res = CoInitialize( NULL );
3969 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3970 msiobj_release(&view->hdr);
3972 if (SUCCEEDED(res)) CoUninitialize();
3973 return rc;
3976 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3978 MSIPACKAGE *package = param;
3979 LPWSTR link_file;
3980 LPCWSTR component;
3981 MSICOMPONENT *comp;
3983 component = MSI_RecordGetString( row, 4 );
3984 comp = msi_get_loaded_component( package, component );
3985 if (!comp)
3986 return ERROR_SUCCESS;
3988 comp->Action = msi_get_component_action( package, comp );
3989 if (comp->Action != INSTALLSTATE_ABSENT)
3991 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3992 return ERROR_SUCCESS;
3994 msi_ui_actiondata( package, szRemoveShortcuts, row );
3996 link_file = get_link_file( package, row );
3998 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3999 if (!DeleteFileW( link_file ))
4001 WARN("Failed to remove shortcut file %u\n", GetLastError());
4003 msi_free( link_file );
4005 return ERROR_SUCCESS;
4008 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4010 static const WCHAR query[] = {
4011 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4012 '`','S','h','o','r','t','c','u','t','`',0};
4013 MSIQUERY *view;
4014 UINT rc;
4016 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4017 if (rc != ERROR_SUCCESS)
4018 return ERROR_SUCCESS;
4020 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4021 msiobj_release( &view->hdr );
4022 return rc;
4025 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4027 MSIPACKAGE* package = param;
4028 HANDLE the_file;
4029 LPWSTR FilePath;
4030 LPCWSTR FileName;
4031 CHAR buffer[1024];
4032 DWORD sz;
4033 UINT rc;
4035 FileName = MSI_RecordGetString(row,1);
4036 if (!FileName)
4038 ERR("Unable to get FileName\n");
4039 return ERROR_SUCCESS;
4042 FilePath = msi_build_icon_path(package, FileName);
4044 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4046 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4047 FILE_ATTRIBUTE_NORMAL, NULL);
4049 if (the_file == INVALID_HANDLE_VALUE)
4051 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4052 msi_free(FilePath);
4053 return ERROR_SUCCESS;
4058 DWORD write;
4059 sz = 1024;
4060 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4061 if (rc != ERROR_SUCCESS)
4063 ERR("Failed to get stream\n");
4064 CloseHandle(the_file);
4065 DeleteFileW(FilePath);
4066 break;
4068 WriteFile(the_file,buffer,sz,&write,NULL);
4069 } while (sz == 1024);
4071 msi_free(FilePath);
4072 CloseHandle(the_file);
4074 return ERROR_SUCCESS;
4077 static UINT msi_publish_icons(MSIPACKAGE *package)
4079 static const WCHAR query[]= {
4080 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4081 '`','I','c','o','n','`',0};
4082 MSIQUERY *view;
4083 UINT r;
4085 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4086 if (r == ERROR_SUCCESS)
4088 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4089 msiobj_release(&view->hdr);
4090 if (r != ERROR_SUCCESS)
4091 return r;
4093 return ERROR_SUCCESS;
4096 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4098 UINT r;
4099 HKEY source;
4100 LPWSTR buffer;
4101 MSIMEDIADISK *disk;
4102 MSISOURCELISTINFO *info;
4104 r = RegCreateKeyW(hkey, szSourceList, &source);
4105 if (r != ERROR_SUCCESS)
4106 return r;
4108 RegCloseKey(source);
4110 buffer = strrchrW(package->PackagePath, '\\') + 1;
4111 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4112 package->Context, MSICODE_PRODUCT,
4113 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4114 if (r != ERROR_SUCCESS)
4115 return r;
4117 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4118 package->Context, MSICODE_PRODUCT,
4119 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4120 if (r != ERROR_SUCCESS)
4121 return r;
4123 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4124 package->Context, MSICODE_PRODUCT,
4125 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4126 if (r != ERROR_SUCCESS)
4127 return r;
4129 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4131 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4132 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4133 info->options, info->value);
4134 else
4135 MsiSourceListSetInfoW(package->ProductCode, NULL,
4136 info->context, info->options,
4137 info->property, info->value);
4140 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4142 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4143 disk->context, disk->options,
4144 disk->disk_id, disk->volume_label, disk->disk_prompt);
4147 return ERROR_SUCCESS;
4150 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4152 MSIHANDLE hdb, suminfo;
4153 WCHAR guids[MAX_PATH];
4154 WCHAR packcode[SQUISH_GUID_SIZE];
4155 LPWSTR buffer;
4156 LPWSTR ptr;
4157 DWORD langid;
4158 DWORD size;
4159 UINT r;
4161 static const WCHAR szARPProductIcon[] =
4162 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4163 static const WCHAR szAssignment[] =
4164 {'A','s','s','i','g','n','m','e','n','t',0};
4165 static const WCHAR szAdvertiseFlags[] =
4166 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4167 static const WCHAR szClients[] =
4168 {'C','l','i','e','n','t','s',0};
4169 static const WCHAR szColon[] = {':',0};
4171 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4172 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4173 msi_free(buffer);
4175 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4176 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4178 /* FIXME */
4179 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4181 buffer = msi_dup_property(package->db, szARPProductIcon);
4182 if (buffer)
4184 LPWSTR path = msi_build_icon_path(package, buffer);
4185 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4186 msi_free(path);
4187 msi_free(buffer);
4190 buffer = msi_dup_property(package->db, szProductVersion);
4191 if (buffer)
4193 DWORD verdword = msi_version_str_to_dword(buffer);
4194 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4195 msi_free(buffer);
4198 msi_reg_set_val_dword(hkey, szAssignment, 0);
4199 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4200 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4201 msi_reg_set_val_str(hkey, szClients, szColon);
4203 hdb = alloc_msihandle(&package->db->hdr);
4204 if (!hdb)
4205 return ERROR_NOT_ENOUGH_MEMORY;
4207 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4208 MsiCloseHandle(hdb);
4209 if (r != ERROR_SUCCESS)
4210 goto done;
4212 size = MAX_PATH;
4213 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4214 NULL, guids, &size);
4215 if (r != ERROR_SUCCESS)
4216 goto done;
4218 ptr = strchrW(guids, ';');
4219 if (ptr) *ptr = 0;
4220 squash_guid(guids, packcode);
4221 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4223 done:
4224 MsiCloseHandle(suminfo);
4225 return ERROR_SUCCESS;
4228 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4230 UINT r;
4231 HKEY hkey;
4232 LPWSTR upgrade;
4233 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4235 upgrade = msi_dup_property(package->db, szUpgradeCode);
4236 if (!upgrade)
4237 return ERROR_SUCCESS;
4239 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4240 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4241 else
4242 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4244 if (r != ERROR_SUCCESS)
4246 WARN("failed to open upgrade code key\n");
4247 msi_free(upgrade);
4248 return ERROR_SUCCESS;
4250 squash_guid(package->ProductCode, squashed_pc);
4251 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4252 RegCloseKey(hkey);
4253 msi_free(upgrade);
4254 return ERROR_SUCCESS;
4257 static BOOL msi_check_publish(MSIPACKAGE *package)
4259 MSIFEATURE *feature;
4261 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4263 feature->Action = msi_get_feature_action( package, feature );
4264 if (feature->Action == INSTALLSTATE_LOCAL)
4265 return TRUE;
4268 return FALSE;
4271 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4273 MSIFEATURE *feature;
4275 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4277 feature->Action = msi_get_feature_action( package, feature );
4278 if (feature->Action != INSTALLSTATE_ABSENT)
4279 return FALSE;
4282 return TRUE;
4285 static UINT msi_publish_patches( MSIPACKAGE *package )
4287 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4288 WCHAR patch_squashed[GUID_SIZE];
4289 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4290 LONG res;
4291 MSIPATCHINFO *patch;
4292 UINT r;
4293 WCHAR *p, *all_patches = NULL;
4294 DWORD len = 0;
4296 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4297 if (r != ERROR_SUCCESS)
4298 return ERROR_FUNCTION_FAILED;
4300 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4301 if (res != ERROR_SUCCESS)
4303 r = ERROR_FUNCTION_FAILED;
4304 goto done;
4307 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4308 if (r != ERROR_SUCCESS)
4309 goto done;
4311 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4313 squash_guid( patch->patchcode, patch_squashed );
4314 len += strlenW( patch_squashed ) + 1;
4317 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4318 if (!all_patches)
4319 goto done;
4321 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4323 HKEY patch_key;
4325 squash_guid( patch->patchcode, p );
4326 p += strlenW( p ) + 1;
4328 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4329 (const BYTE *)patch->transforms,
4330 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4331 if (res != ERROR_SUCCESS)
4332 goto done;
4334 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4335 if (r != ERROR_SUCCESS)
4336 goto done;
4338 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4339 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4340 RegCloseKey( patch_key );
4341 if (res != ERROR_SUCCESS)
4342 goto done;
4344 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4346 res = GetLastError();
4347 ERR("Unable to copy patch package %d\n", res);
4348 goto done;
4350 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4351 if (res != ERROR_SUCCESS)
4352 goto done;
4354 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4355 RegCloseKey( patch_key );
4356 if (res != ERROR_SUCCESS)
4357 goto done;
4360 all_patches[len] = 0;
4361 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4362 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4363 if (res != ERROR_SUCCESS)
4364 goto done;
4366 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4367 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4368 if (res != ERROR_SUCCESS)
4369 r = ERROR_FUNCTION_FAILED;
4371 done:
4372 RegCloseKey( product_patches_key );
4373 RegCloseKey( patches_key );
4374 RegCloseKey( product_key );
4375 msi_free( all_patches );
4376 return r;
4379 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4381 UINT rc;
4382 HKEY hukey = NULL, hudkey = NULL;
4383 MSIRECORD *uirow;
4385 if (!list_empty(&package->patches))
4387 rc = msi_publish_patches(package);
4388 if (rc != ERROR_SUCCESS)
4389 goto end;
4392 /* FIXME: also need to publish if the product is in advertise mode */
4393 if (!msi_check_publish(package))
4394 return ERROR_SUCCESS;
4396 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4397 &hukey, TRUE);
4398 if (rc != ERROR_SUCCESS)
4399 goto end;
4401 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4402 NULL, &hudkey, TRUE);
4403 if (rc != ERROR_SUCCESS)
4404 goto end;
4406 rc = msi_publish_upgrade_code(package);
4407 if (rc != ERROR_SUCCESS)
4408 goto end;
4410 rc = msi_publish_product_properties(package, hukey);
4411 if (rc != ERROR_SUCCESS)
4412 goto end;
4414 rc = msi_publish_sourcelist(package, hukey);
4415 if (rc != ERROR_SUCCESS)
4416 goto end;
4418 rc = msi_publish_icons(package);
4420 end:
4421 uirow = MSI_CreateRecord( 1 );
4422 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4423 msi_ui_actiondata( package, szPublishProduct, uirow );
4424 msiobj_release( &uirow->hdr );
4426 RegCloseKey(hukey);
4427 RegCloseKey(hudkey);
4428 return rc;
4431 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4433 WCHAR *filename, *ptr, *folder, *ret;
4434 const WCHAR *dirprop;
4436 filename = msi_dup_record_field( row, 2 );
4437 if (filename && (ptr = strchrW( filename, '|' )))
4438 ptr++;
4439 else
4440 ptr = filename;
4442 dirprop = MSI_RecordGetString( row, 3 );
4443 if (dirprop)
4445 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4446 if (!folder) folder = msi_dup_property( package->db, dirprop );
4448 else
4449 folder = msi_dup_property( package->db, szWindowsFolder );
4451 if (!folder)
4453 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4454 msi_free( filename );
4455 return NULL;
4458 ret = msi_build_directory_name( 2, folder, ptr );
4460 msi_free( filename );
4461 msi_free( folder );
4462 return ret;
4465 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4467 MSIPACKAGE *package = param;
4468 LPCWSTR component, section, key, value, identifier;
4469 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4470 MSIRECORD * uirow;
4471 INT action;
4472 MSICOMPONENT *comp;
4474 component = MSI_RecordGetString(row, 8);
4475 comp = msi_get_loaded_component(package,component);
4476 if (!comp)
4477 return ERROR_SUCCESS;
4479 comp->Action = msi_get_component_action( package, comp );
4480 if (comp->Action != INSTALLSTATE_LOCAL)
4482 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4483 return ERROR_SUCCESS;
4486 identifier = MSI_RecordGetString(row,1);
4487 section = MSI_RecordGetString(row,4);
4488 key = MSI_RecordGetString(row,5);
4489 value = MSI_RecordGetString(row,6);
4490 action = MSI_RecordGetInteger(row,7);
4492 deformat_string(package,section,&deformated_section);
4493 deformat_string(package,key,&deformated_key);
4494 deformat_string(package,value,&deformated_value);
4496 fullname = get_ini_file_name(package, row);
4498 if (action == 0)
4500 TRACE("Adding value %s to section %s in %s\n",
4501 debugstr_w(deformated_key), debugstr_w(deformated_section),
4502 debugstr_w(fullname));
4503 WritePrivateProfileStringW(deformated_section, deformated_key,
4504 deformated_value, fullname);
4506 else if (action == 1)
4508 WCHAR returned[10];
4509 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4510 returned, 10, fullname);
4511 if (returned[0] == 0)
4513 TRACE("Adding value %s to section %s in %s\n",
4514 debugstr_w(deformated_key), debugstr_w(deformated_section),
4515 debugstr_w(fullname));
4517 WritePrivateProfileStringW(deformated_section, deformated_key,
4518 deformated_value, fullname);
4521 else if (action == 3)
4522 FIXME("Append to existing section not yet implemented\n");
4524 uirow = MSI_CreateRecord(4);
4525 MSI_RecordSetStringW(uirow,1,identifier);
4526 MSI_RecordSetStringW(uirow,2,deformated_section);
4527 MSI_RecordSetStringW(uirow,3,deformated_key);
4528 MSI_RecordSetStringW(uirow,4,deformated_value);
4529 msi_ui_actiondata( package, szWriteIniValues, uirow );
4530 msiobj_release( &uirow->hdr );
4532 msi_free(fullname);
4533 msi_free(deformated_key);
4534 msi_free(deformated_value);
4535 msi_free(deformated_section);
4536 return ERROR_SUCCESS;
4539 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4541 static const WCHAR query[] = {
4542 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4543 '`','I','n','i','F','i','l','e','`',0};
4544 MSIQUERY *view;
4545 UINT rc;
4547 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4548 if (rc != ERROR_SUCCESS)
4549 return ERROR_SUCCESS;
4551 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4552 msiobj_release(&view->hdr);
4553 return rc;
4556 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4558 MSIPACKAGE *package = param;
4559 LPCWSTR component, section, key, value, identifier;
4560 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4561 MSICOMPONENT *comp;
4562 MSIRECORD *uirow;
4563 INT action;
4565 component = MSI_RecordGetString( row, 8 );
4566 comp = msi_get_loaded_component( package, component );
4567 if (!comp)
4568 return ERROR_SUCCESS;
4570 comp->Action = msi_get_component_action( package, comp );
4571 if (comp->Action != INSTALLSTATE_ABSENT)
4573 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4574 return ERROR_SUCCESS;
4577 identifier = MSI_RecordGetString( row, 1 );
4578 section = MSI_RecordGetString( row, 4 );
4579 key = MSI_RecordGetString( row, 5 );
4580 value = MSI_RecordGetString( row, 6 );
4581 action = MSI_RecordGetInteger( row, 7 );
4583 deformat_string( package, section, &deformated_section );
4584 deformat_string( package, key, &deformated_key );
4585 deformat_string( package, value, &deformated_value );
4587 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4589 filename = get_ini_file_name( package, row );
4591 TRACE("Removing key %s from section %s in %s\n",
4592 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4594 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4596 WARN("Unable to remove key %u\n", GetLastError());
4598 msi_free( filename );
4600 else
4601 FIXME("Unsupported action %d\n", action);
4604 uirow = MSI_CreateRecord( 4 );
4605 MSI_RecordSetStringW( uirow, 1, identifier );
4606 MSI_RecordSetStringW( uirow, 2, deformated_section );
4607 MSI_RecordSetStringW( uirow, 3, deformated_key );
4608 MSI_RecordSetStringW( uirow, 4, deformated_value );
4609 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4610 msiobj_release( &uirow->hdr );
4612 msi_free( deformated_key );
4613 msi_free( deformated_value );
4614 msi_free( deformated_section );
4615 return ERROR_SUCCESS;
4618 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4620 MSIPACKAGE *package = param;
4621 LPCWSTR component, section, key, value, identifier;
4622 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4623 MSICOMPONENT *comp;
4624 MSIRECORD *uirow;
4625 INT action;
4627 component = MSI_RecordGetString( row, 8 );
4628 comp = msi_get_loaded_component( package, component );
4629 if (!comp)
4630 return ERROR_SUCCESS;
4632 comp->Action = msi_get_component_action( package, comp );
4633 if (comp->Action != INSTALLSTATE_LOCAL)
4635 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4636 return ERROR_SUCCESS;
4639 identifier = MSI_RecordGetString( row, 1 );
4640 section = MSI_RecordGetString( row, 4 );
4641 key = MSI_RecordGetString( row, 5 );
4642 value = MSI_RecordGetString( row, 6 );
4643 action = MSI_RecordGetInteger( row, 7 );
4645 deformat_string( package, section, &deformated_section );
4646 deformat_string( package, key, &deformated_key );
4647 deformat_string( package, value, &deformated_value );
4649 if (action == msidbIniFileActionRemoveLine)
4651 filename = get_ini_file_name( package, row );
4653 TRACE("Removing key %s from section %s in %s\n",
4654 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4656 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4658 WARN("Unable to remove key %u\n", GetLastError());
4660 msi_free( filename );
4662 else
4663 FIXME("Unsupported action %d\n", action);
4665 uirow = MSI_CreateRecord( 4 );
4666 MSI_RecordSetStringW( uirow, 1, identifier );
4667 MSI_RecordSetStringW( uirow, 2, deformated_section );
4668 MSI_RecordSetStringW( uirow, 3, deformated_key );
4669 MSI_RecordSetStringW( uirow, 4, deformated_value );
4670 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4671 msiobj_release( &uirow->hdr );
4673 msi_free( deformated_key );
4674 msi_free( deformated_value );
4675 msi_free( deformated_section );
4676 return ERROR_SUCCESS;
4679 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4681 static const WCHAR query[] = {
4682 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4683 '`','I','n','i','F','i','l','e','`',0};
4684 static const WCHAR remove_query[] = {
4685 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4686 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4687 MSIQUERY *view;
4688 UINT rc;
4690 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4691 if (rc == ERROR_SUCCESS)
4693 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4694 msiobj_release( &view->hdr );
4695 if (rc != ERROR_SUCCESS)
4696 return rc;
4698 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4699 if (rc == ERROR_SUCCESS)
4701 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4702 msiobj_release( &view->hdr );
4703 if (rc != ERROR_SUCCESS)
4704 return rc;
4706 return ERROR_SUCCESS;
4709 static void register_dll( const WCHAR *dll, BOOL unregister )
4711 static const WCHAR regW[] =
4712 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4713 static const WCHAR unregW[] =
4714 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4715 PROCESS_INFORMATION pi;
4716 STARTUPINFOW si;
4717 WCHAR *cmd;
4719 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4721 if (unregister) sprintfW( cmd, unregW, dll );
4722 else sprintfW( cmd, regW, dll );
4724 memset( &si, 0, sizeof(STARTUPINFOW) );
4725 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4727 CloseHandle( pi.hThread );
4728 msi_dialog_check_messages( pi.hProcess );
4729 CloseHandle( pi.hProcess );
4731 msi_free( cmd );
4734 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4736 MSIPACKAGE *package = param;
4737 LPCWSTR filename;
4738 MSIFILE *file;
4739 MSIRECORD *uirow;
4741 filename = MSI_RecordGetString( row, 1 );
4742 file = msi_get_loaded_file( package, filename );
4743 if (!file)
4745 WARN("unable to find file %s\n", debugstr_w(filename));
4746 return ERROR_SUCCESS;
4748 file->Component->Action = msi_get_component_action( package, file->Component );
4749 if (file->Component->Action != INSTALLSTATE_LOCAL)
4751 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4752 return ERROR_SUCCESS;
4755 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4756 register_dll( file->TargetPath, FALSE );
4758 uirow = MSI_CreateRecord( 2 );
4759 MSI_RecordSetStringW( uirow, 1, file->File );
4760 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4761 msi_ui_actiondata( package, szSelfRegModules, uirow );
4762 msiobj_release( &uirow->hdr );
4764 return ERROR_SUCCESS;
4767 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4769 static const WCHAR query[] = {
4770 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4771 '`','S','e','l','f','R','e','g','`',0};
4772 MSIQUERY *view;
4773 UINT rc;
4775 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4776 if (rc != ERROR_SUCCESS)
4777 return ERROR_SUCCESS;
4779 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4780 msiobj_release(&view->hdr);
4781 return rc;
4784 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4786 MSIPACKAGE *package = param;
4787 LPCWSTR filename;
4788 MSIFILE *file;
4789 MSIRECORD *uirow;
4791 filename = MSI_RecordGetString( row, 1 );
4792 file = msi_get_loaded_file( package, filename );
4793 if (!file)
4795 WARN("unable to find file %s\n", debugstr_w(filename));
4796 return ERROR_SUCCESS;
4798 file->Component->Action = msi_get_component_action( package, file->Component );
4799 if (file->Component->Action != INSTALLSTATE_ABSENT)
4801 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4802 return ERROR_SUCCESS;
4805 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4806 register_dll( file->TargetPath, TRUE );
4808 uirow = MSI_CreateRecord( 2 );
4809 MSI_RecordSetStringW( uirow, 1, file->File );
4810 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4811 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4812 msiobj_release( &uirow->hdr );
4814 return ERROR_SUCCESS;
4817 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4819 static const WCHAR query[] = {
4820 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4821 '`','S','e','l','f','R','e','g','`',0};
4822 MSIQUERY *view;
4823 UINT rc;
4825 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4826 if (rc != ERROR_SUCCESS)
4827 return ERROR_SUCCESS;
4829 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4830 msiobj_release( &view->hdr );
4831 return rc;
4834 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4836 MSIFEATURE *feature;
4837 UINT rc;
4838 HKEY hkey = NULL, userdata = NULL;
4840 if (!msi_check_publish(package))
4841 return ERROR_SUCCESS;
4843 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4844 &hkey, TRUE);
4845 if (rc != ERROR_SUCCESS)
4846 goto end;
4848 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4849 &userdata, TRUE);
4850 if (rc != ERROR_SUCCESS)
4851 goto end;
4853 /* here the guids are base 85 encoded */
4854 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4856 ComponentList *cl;
4857 LPWSTR data = NULL;
4858 GUID clsid;
4859 INT size;
4860 BOOL absent = FALSE;
4861 MSIRECORD *uirow;
4863 if (feature->Action != INSTALLSTATE_LOCAL &&
4864 feature->Action != INSTALLSTATE_SOURCE &&
4865 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4867 size = 1;
4868 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4870 size += 21;
4872 if (feature->Feature_Parent)
4873 size += strlenW( feature->Feature_Parent )+2;
4875 data = msi_alloc(size * sizeof(WCHAR));
4877 data[0] = 0;
4878 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4880 MSICOMPONENT* component = cl->component;
4881 WCHAR buf[21];
4883 buf[0] = 0;
4884 if (component->ComponentId)
4886 TRACE("From %s\n",debugstr_w(component->ComponentId));
4887 CLSIDFromString(component->ComponentId, &clsid);
4888 encode_base85_guid(&clsid,buf);
4889 TRACE("to %s\n",debugstr_w(buf));
4890 strcatW(data,buf);
4894 if (feature->Feature_Parent)
4896 static const WCHAR sep[] = {'\2',0};
4897 strcatW(data,sep);
4898 strcatW(data,feature->Feature_Parent);
4901 msi_reg_set_val_str( userdata, feature->Feature, data );
4902 msi_free(data);
4904 size = 0;
4905 if (feature->Feature_Parent)
4906 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4907 if (!absent)
4909 size += sizeof(WCHAR);
4910 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4911 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4913 else
4915 size += 2*sizeof(WCHAR);
4916 data = msi_alloc(size);
4917 data[0] = 0x6;
4918 data[1] = 0;
4919 if (feature->Feature_Parent)
4920 strcpyW( &data[1], feature->Feature_Parent );
4921 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4922 (LPBYTE)data,size);
4923 msi_free(data);
4926 /* the UI chunk */
4927 uirow = MSI_CreateRecord( 1 );
4928 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4929 msi_ui_actiondata( package, szPublishFeatures, uirow );
4930 msiobj_release( &uirow->hdr );
4931 /* FIXME: call msi_ui_progress? */
4934 end:
4935 RegCloseKey(hkey);
4936 RegCloseKey(userdata);
4937 return rc;
4940 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4942 UINT r;
4943 HKEY hkey;
4944 MSIRECORD *uirow;
4946 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4948 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4949 &hkey, FALSE);
4950 if (r == ERROR_SUCCESS)
4952 RegDeleteValueW(hkey, feature->Feature);
4953 RegCloseKey(hkey);
4956 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4957 &hkey, FALSE);
4958 if (r == ERROR_SUCCESS)
4960 RegDeleteValueW(hkey, feature->Feature);
4961 RegCloseKey(hkey);
4964 uirow = MSI_CreateRecord( 1 );
4965 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4966 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4967 msiobj_release( &uirow->hdr );
4969 return ERROR_SUCCESS;
4972 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4974 MSIFEATURE *feature;
4976 if (!msi_check_unpublish(package))
4977 return ERROR_SUCCESS;
4979 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4981 msi_unpublish_feature(package, feature);
4984 return ERROR_SUCCESS;
4987 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4989 SYSTEMTIME systime;
4990 DWORD size, langid;
4991 WCHAR date[9], *val, *buffer;
4992 const WCHAR *prop, *key;
4994 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4995 static const WCHAR modpath_fmt[] =
4996 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4997 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4998 static const WCHAR szModifyPath[] =
4999 {'M','o','d','i','f','y','P','a','t','h',0};
5000 static const WCHAR szUninstallString[] =
5001 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5002 static const WCHAR szEstimatedSize[] =
5003 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5004 static const WCHAR szDisplayVersion[] =
5005 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5006 static const WCHAR szInstallSource[] =
5007 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5008 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5009 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5010 static const WCHAR szAuthorizedCDFPrefix[] =
5011 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5012 static const WCHAR szARPCONTACT[] =
5013 {'A','R','P','C','O','N','T','A','C','T',0};
5014 static const WCHAR szContact[] =
5015 {'C','o','n','t','a','c','t',0};
5016 static const WCHAR szARPCOMMENTS[] =
5017 {'A','R','P','C','O','M','M','E','N','T','S',0};
5018 static const WCHAR szComments[] =
5019 {'C','o','m','m','e','n','t','s',0};
5020 static const WCHAR szProductName[] =
5021 {'P','r','o','d','u','c','t','N','a','m','e',0};
5022 static const WCHAR szDisplayName[] =
5023 {'D','i','s','p','l','a','y','N','a','m','e',0};
5024 static const WCHAR szARPHELPLINK[] =
5025 {'A','R','P','H','E','L','P','L','I','N','K',0};
5026 static const WCHAR szHelpLink[] =
5027 {'H','e','l','p','L','i','n','k',0};
5028 static const WCHAR szARPHELPTELEPHONE[] =
5029 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5030 static const WCHAR szHelpTelephone[] =
5031 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5032 static const WCHAR szARPINSTALLLOCATION[] =
5033 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5034 static const WCHAR szManufacturer[] =
5035 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5036 static const WCHAR szPublisher[] =
5037 {'P','u','b','l','i','s','h','e','r',0};
5038 static const WCHAR szARPREADME[] =
5039 {'A','R','P','R','E','A','D','M','E',0};
5040 static const WCHAR szReadme[] =
5041 {'R','e','a','d','M','e',0};
5042 static const WCHAR szARPSIZE[] =
5043 {'A','R','P','S','I','Z','E',0};
5044 static const WCHAR szSize[] =
5045 {'S','i','z','e',0};
5046 static const WCHAR szARPURLINFOABOUT[] =
5047 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5048 static const WCHAR szURLInfoAbout[] =
5049 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5050 static const WCHAR szARPURLUPDATEINFO[] =
5051 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5052 static const WCHAR szURLUpdateInfo[] =
5053 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5054 static const WCHAR szARPSYSTEMCOMPONENT[] =
5055 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5056 static const WCHAR szSystemComponent[] =
5057 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5059 static const WCHAR *propval[] = {
5060 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5061 szARPCONTACT, szContact,
5062 szARPCOMMENTS, szComments,
5063 szProductName, szDisplayName,
5064 szARPHELPLINK, szHelpLink,
5065 szARPHELPTELEPHONE, szHelpTelephone,
5066 szARPINSTALLLOCATION, szInstallLocation,
5067 szSourceDir, szInstallSource,
5068 szManufacturer, szPublisher,
5069 szARPREADME, szReadme,
5070 szARPSIZE, szSize,
5071 szARPURLINFOABOUT, szURLInfoAbout,
5072 szARPURLUPDATEINFO, szURLUpdateInfo,
5073 NULL
5075 const WCHAR **p = propval;
5077 while (*p)
5079 prop = *p++;
5080 key = *p++;
5081 val = msi_dup_property(package->db, prop);
5082 msi_reg_set_val_str(hkey, key, val);
5083 msi_free(val);
5086 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5087 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5089 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5091 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5092 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5093 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5094 msi_free(buffer);
5096 /* FIXME: Write real Estimated Size when we have it */
5097 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5099 GetLocalTime(&systime);
5100 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5101 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5103 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5104 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5106 buffer = msi_dup_property(package->db, szProductVersion);
5107 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5108 if (buffer)
5110 DWORD verdword = msi_version_str_to_dword(buffer);
5112 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5113 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5114 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5115 msi_free(buffer);
5118 return ERROR_SUCCESS;
5121 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5123 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5124 MSIRECORD *uirow;
5125 LPWSTR upgrade_code;
5126 HKEY hkey, props, upgrade_key;
5127 UINT rc;
5129 /* FIXME: also need to publish if the product is in advertise mode */
5130 if (!msi_check_publish(package))
5131 return ERROR_SUCCESS;
5133 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5134 if (rc != ERROR_SUCCESS)
5135 return rc;
5137 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5138 if (rc != ERROR_SUCCESS)
5139 goto done;
5141 rc = msi_publish_install_properties(package, hkey);
5142 if (rc != ERROR_SUCCESS)
5143 goto done;
5145 rc = msi_publish_install_properties(package, props);
5146 if (rc != ERROR_SUCCESS)
5147 goto done;
5149 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5150 if (upgrade_code)
5152 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5153 if (rc == ERROR_SUCCESS)
5155 squash_guid( package->ProductCode, squashed_pc );
5156 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5157 RegCloseKey( upgrade_key );
5159 msi_free( upgrade_code );
5161 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5162 package->delete_on_close = FALSE;
5164 done:
5165 uirow = MSI_CreateRecord( 1 );
5166 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5167 msi_ui_actiondata( package, szRegisterProduct, uirow );
5168 msiobj_release( &uirow->hdr );
5170 RegCloseKey(hkey);
5171 return ERROR_SUCCESS;
5174 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5176 return execute_script(package, SCRIPT_INSTALL);
5179 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5181 MSIPACKAGE *package = param;
5182 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5183 WCHAR *p, *icon_path;
5185 if (!icon) return ERROR_SUCCESS;
5186 if ((icon_path = msi_build_icon_path( package, icon )))
5188 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5189 DeleteFileW( icon_path );
5190 if ((p = strrchrW( icon_path, '\\' )))
5192 *p = 0;
5193 RemoveDirectoryW( icon_path );
5195 msi_free( icon_path );
5197 return ERROR_SUCCESS;
5200 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5202 static const WCHAR query[]= {
5203 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5204 MSIQUERY *view;
5205 UINT r;
5207 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5208 if (r == ERROR_SUCCESS)
5210 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5211 msiobj_release( &view->hdr );
5212 if (r != ERROR_SUCCESS)
5213 return r;
5215 return ERROR_SUCCESS;
5218 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5220 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5221 WCHAR *upgrade, **features;
5222 BOOL full_uninstall = TRUE;
5223 MSIFEATURE *feature;
5224 MSIPATCHINFO *patch;
5225 UINT i;
5227 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5229 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5231 features = msi_split_string( remove, ',' );
5232 for (i = 0; features && features[i]; i++)
5234 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5236 msi_free(features);
5238 if (!full_uninstall)
5239 return ERROR_SUCCESS;
5241 MSIREG_DeleteProductKey(package->ProductCode);
5242 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5243 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5245 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5246 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5247 MSIREG_DeleteUserProductKey(package->ProductCode);
5248 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5250 upgrade = msi_dup_property(package->db, szUpgradeCode);
5251 if (upgrade)
5253 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5254 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5255 msi_free(upgrade);
5258 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5260 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5261 if (!strcmpW( package->ProductCode, patch->products ))
5263 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5264 patch->delete_on_close = TRUE;
5266 /* FIXME: remove local patch package if this is the last product */
5268 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5269 package->delete_on_close = TRUE;
5271 msi_unpublish_icons( package );
5272 return ERROR_SUCCESS;
5275 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5277 UINT rc;
5278 WCHAR *remove;
5280 /* turn off scheduling */
5281 package->script->CurrentlyScripting= FALSE;
5283 /* first do the same as an InstallExecute */
5284 rc = ACTION_InstallExecute(package);
5285 if (rc != ERROR_SUCCESS)
5286 return rc;
5288 /* then handle commit actions */
5289 rc = execute_script(package, SCRIPT_COMMIT);
5290 if (rc != ERROR_SUCCESS)
5291 return rc;
5293 remove = msi_dup_property(package->db, szRemove);
5294 rc = msi_unpublish_product(package, remove);
5295 msi_free(remove);
5296 return rc;
5299 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5301 static const WCHAR RunOnce[] = {
5302 'S','o','f','t','w','a','r','e','\\',
5303 'M','i','c','r','o','s','o','f','t','\\',
5304 'W','i','n','d','o','w','s','\\',
5305 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5306 'R','u','n','O','n','c','e',0};
5307 static const WCHAR InstallRunOnce[] = {
5308 'S','o','f','t','w','a','r','e','\\',
5309 'M','i','c','r','o','s','o','f','t','\\',
5310 'W','i','n','d','o','w','s','\\',
5311 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5312 'I','n','s','t','a','l','l','e','r','\\',
5313 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5315 static const WCHAR msiexec_fmt[] = {
5316 '%','s',
5317 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5318 '\"','%','s','\"',0};
5319 static const WCHAR install_fmt[] = {
5320 '/','I',' ','\"','%','s','\"',' ',
5321 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5322 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5323 WCHAR buffer[256], sysdir[MAX_PATH];
5324 HKEY hkey;
5325 WCHAR squished_pc[100];
5327 squash_guid(package->ProductCode,squished_pc);
5329 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5330 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5331 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5332 squished_pc);
5334 msi_reg_set_val_str( hkey, squished_pc, buffer );
5335 RegCloseKey(hkey);
5337 TRACE("Reboot command %s\n",debugstr_w(buffer));
5339 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5340 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5342 msi_reg_set_val_str( hkey, squished_pc, buffer );
5343 RegCloseKey(hkey);
5345 return ERROR_INSTALL_SUSPEND;
5348 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5350 static const WCHAR query[] =
5351 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5352 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5353 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5354 MSIRECORD *rec, *row;
5355 DWORD i, size = 0;
5356 va_list va;
5357 const WCHAR *str;
5358 WCHAR *data;
5360 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5362 rec = MSI_CreateRecord( count + 2 );
5363 str = MSI_RecordGetString( row, 1 );
5364 MSI_RecordSetStringW( rec, 0, str );
5365 msiobj_release( &row->hdr );
5366 MSI_RecordSetInteger( rec, 1, error );
5368 va_start( va, count );
5369 for (i = 0; i < count; i++)
5371 str = va_arg( va, const WCHAR *);
5372 MSI_RecordSetStringW( rec, i + 2, str );
5374 va_end( va );
5376 MSI_FormatRecordW( package, rec, NULL, &size );
5377 size++;
5378 data = msi_alloc( size * sizeof(WCHAR) );
5379 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5380 else data[0] = 0;
5381 msiobj_release( &rec->hdr );
5382 return data;
5385 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5387 DWORD attrib;
5388 UINT rc;
5391 * We are currently doing what should be done here in the top level Install
5392 * however for Administrative and uninstalls this step will be needed
5394 if (!package->PackagePath)
5395 return ERROR_SUCCESS;
5397 msi_set_sourcedir_props(package, TRUE);
5399 attrib = GetFileAttributesW(package->db->path);
5400 if (attrib == INVALID_FILE_ATTRIBUTES)
5402 LPWSTR prompt, msg;
5403 DWORD size = 0;
5405 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5406 package->Context, MSICODE_PRODUCT,
5407 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5408 if (rc == ERROR_MORE_DATA)
5410 prompt = msi_alloc(size * sizeof(WCHAR));
5411 MsiSourceListGetInfoW(package->ProductCode, NULL,
5412 package->Context, MSICODE_PRODUCT,
5413 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5415 else
5416 prompt = strdupW(package->db->path);
5418 msg = msi_build_error_string(package, 1302, 1, prompt);
5419 msi_free(prompt);
5420 while(attrib == INVALID_FILE_ATTRIBUTES)
5422 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5423 if (rc == IDCANCEL)
5425 msi_free(msg);
5426 return ERROR_INSTALL_USEREXIT;
5428 attrib = GetFileAttributesW(package->db->path);
5430 msi_free(msg);
5431 rc = ERROR_SUCCESS;
5433 else
5434 return ERROR_SUCCESS;
5436 return rc;
5439 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5441 HKEY hkey = 0;
5442 LPWSTR buffer, productid = NULL;
5443 UINT i, rc = ERROR_SUCCESS;
5444 MSIRECORD *uirow;
5446 static const WCHAR szPropKeys[][80] =
5448 {'P','r','o','d','u','c','t','I','D',0},
5449 {'U','S','E','R','N','A','M','E',0},
5450 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5451 {0},
5454 static const WCHAR szRegKeys[][80] =
5456 {'P','r','o','d','u','c','t','I','D',0},
5457 {'R','e','g','O','w','n','e','r',0},
5458 {'R','e','g','C','o','m','p','a','n','y',0},
5459 {0},
5462 if (msi_check_unpublish(package))
5464 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5465 goto end;
5468 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5469 if (!productid)
5470 goto end;
5472 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5473 NULL, &hkey, TRUE);
5474 if (rc != ERROR_SUCCESS)
5475 goto end;
5477 for( i = 0; szPropKeys[i][0]; i++ )
5479 buffer = msi_dup_property( package->db, szPropKeys[i] );
5480 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5481 msi_free( buffer );
5484 end:
5485 uirow = MSI_CreateRecord( 1 );
5486 MSI_RecordSetStringW( uirow, 1, productid );
5487 msi_ui_actiondata( package, szRegisterUser, uirow );
5488 msiobj_release( &uirow->hdr );
5490 msi_free(productid);
5491 RegCloseKey(hkey);
5492 return rc;
5496 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5498 UINT rc;
5500 package->script->InWhatSequence |= SEQUENCE_EXEC;
5501 rc = ACTION_ProcessExecSequence(package,FALSE);
5502 return rc;
5505 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5507 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5508 WCHAR productid_85[21], component_85[21], *ret;
5509 GUID clsid;
5510 DWORD sz;
5512 /* > is used if there is a component GUID and < if not. */
5514 productid_85[0] = 0;
5515 component_85[0] = 0;
5516 CLSIDFromString( package->ProductCode, &clsid );
5518 encode_base85_guid( &clsid, productid_85 );
5519 if (component)
5521 CLSIDFromString( component->ComponentId, &clsid );
5522 encode_base85_guid( &clsid, component_85 );
5525 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5526 debugstr_w(component_85));
5528 sz = 20 + strlenW( feature ) + 20 + 3;
5529 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5530 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5531 return ret;
5534 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5536 MSIPACKAGE *package = param;
5537 LPCWSTR compgroupid, component, feature, qualifier, text;
5538 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5539 HKEY hkey = NULL;
5540 UINT rc;
5541 MSICOMPONENT *comp;
5542 MSIFEATURE *feat;
5543 DWORD sz;
5544 MSIRECORD *uirow;
5545 int len;
5547 feature = MSI_RecordGetString(rec, 5);
5548 feat = msi_get_loaded_feature(package, feature);
5549 if (!feat)
5550 return ERROR_SUCCESS;
5552 feat->Action = msi_get_feature_action( package, feat );
5553 if (feat->Action != INSTALLSTATE_LOCAL &&
5554 feat->Action != INSTALLSTATE_SOURCE &&
5555 feat->Action != INSTALLSTATE_ADVERTISED)
5557 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5558 return ERROR_SUCCESS;
5561 component = MSI_RecordGetString(rec, 3);
5562 comp = msi_get_loaded_component(package, component);
5563 if (!comp)
5564 return ERROR_SUCCESS;
5566 compgroupid = MSI_RecordGetString(rec,1);
5567 qualifier = MSI_RecordGetString(rec,2);
5569 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5570 if (rc != ERROR_SUCCESS)
5571 goto end;
5573 advertise = msi_create_component_advertise_string( package, comp, feature );
5574 text = MSI_RecordGetString( rec, 4 );
5575 if (text)
5577 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5578 strcpyW( p, advertise );
5579 strcatW( p, text );
5580 msi_free( advertise );
5581 advertise = p;
5583 existing = msi_reg_get_val_str( hkey, qualifier );
5585 sz = strlenW( advertise ) + 1;
5586 if (existing)
5588 for (p = existing; *p; p += len)
5590 len = strlenW( p ) + 1;
5591 if (strcmpW( advertise, p )) sz += len;
5594 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5596 rc = ERROR_OUTOFMEMORY;
5597 goto end;
5599 q = output;
5600 if (existing)
5602 for (p = existing; *p; p += len)
5604 len = strlenW( p ) + 1;
5605 if (strcmpW( advertise, p ))
5607 memcpy( q, p, len * sizeof(WCHAR) );
5608 q += len;
5612 strcpyW( q, advertise );
5613 q[strlenW( q ) + 1] = 0;
5615 msi_reg_set_val_multi_str( hkey, qualifier, output );
5617 end:
5618 RegCloseKey(hkey);
5619 msi_free( output );
5620 msi_free( advertise );
5621 msi_free( existing );
5623 /* the UI chunk */
5624 uirow = MSI_CreateRecord( 2 );
5625 MSI_RecordSetStringW( uirow, 1, compgroupid );
5626 MSI_RecordSetStringW( uirow, 2, qualifier);
5627 msi_ui_actiondata( package, szPublishComponents, uirow );
5628 msiobj_release( &uirow->hdr );
5629 /* FIXME: call ui_progress? */
5631 return rc;
5635 * At present I am ignorning the advertised components part of this and only
5636 * focusing on the qualified component sets
5638 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5640 static const WCHAR query[] = {
5641 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5642 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5643 MSIQUERY *view;
5644 UINT rc;
5646 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5647 if (rc != ERROR_SUCCESS)
5648 return ERROR_SUCCESS;
5650 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5651 msiobj_release(&view->hdr);
5652 return rc;
5655 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5657 static const WCHAR szInstallerComponents[] = {
5658 'S','o','f','t','w','a','r','e','\\',
5659 'M','i','c','r','o','s','o','f','t','\\',
5660 'I','n','s','t','a','l','l','e','r','\\',
5661 'C','o','m','p','o','n','e','n','t','s','\\',0};
5663 MSIPACKAGE *package = param;
5664 LPCWSTR compgroupid, component, feature, qualifier;
5665 MSICOMPONENT *comp;
5666 MSIFEATURE *feat;
5667 MSIRECORD *uirow;
5668 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5669 LONG res;
5671 feature = MSI_RecordGetString( rec, 5 );
5672 feat = msi_get_loaded_feature( package, feature );
5673 if (!feat)
5674 return ERROR_SUCCESS;
5676 feat->Action = msi_get_feature_action( package, feat );
5677 if (feat->Action != INSTALLSTATE_ABSENT)
5679 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5680 return ERROR_SUCCESS;
5683 component = MSI_RecordGetString( rec, 3 );
5684 comp = msi_get_loaded_component( package, component );
5685 if (!comp)
5686 return ERROR_SUCCESS;
5688 compgroupid = MSI_RecordGetString( rec, 1 );
5689 qualifier = MSI_RecordGetString( rec, 2 );
5691 squash_guid( compgroupid, squashed );
5692 strcpyW( keypath, szInstallerComponents );
5693 strcatW( keypath, squashed );
5695 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5696 if (res != ERROR_SUCCESS)
5698 WARN("Unable to delete component key %d\n", res);
5701 uirow = MSI_CreateRecord( 2 );
5702 MSI_RecordSetStringW( uirow, 1, compgroupid );
5703 MSI_RecordSetStringW( uirow, 2, qualifier );
5704 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5705 msiobj_release( &uirow->hdr );
5707 return ERROR_SUCCESS;
5710 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5712 static const WCHAR query[] = {
5713 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5714 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5715 MSIQUERY *view;
5716 UINT rc;
5718 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5719 if (rc != ERROR_SUCCESS)
5720 return ERROR_SUCCESS;
5722 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5723 msiobj_release( &view->hdr );
5724 return rc;
5727 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5729 static const WCHAR query[] =
5730 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5731 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5732 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5733 MSIPACKAGE *package = param;
5734 MSICOMPONENT *component;
5735 MSIRECORD *row;
5736 MSIFILE *file;
5737 SC_HANDLE hscm = NULL, service = NULL;
5738 LPCWSTR comp, key;
5739 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5740 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5741 DWORD serv_type, start_type, err_control;
5742 SERVICE_DESCRIPTIONW sd = {NULL};
5743 UINT ret = ERROR_SUCCESS;
5745 comp = MSI_RecordGetString( rec, 12 );
5746 component = msi_get_loaded_component( package, comp );
5747 if (!component)
5749 WARN("service component not found\n");
5750 goto done;
5752 component->Action = msi_get_component_action( package, component );
5753 if (component->Action != INSTALLSTATE_LOCAL)
5755 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5756 goto done;
5758 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5759 if (!hscm)
5761 ERR("Failed to open the SC Manager!\n");
5762 goto done;
5765 start_type = MSI_RecordGetInteger(rec, 5);
5766 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5767 goto done;
5769 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5770 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5771 serv_type = MSI_RecordGetInteger(rec, 4);
5772 err_control = MSI_RecordGetInteger(rec, 6);
5773 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5774 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5775 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5776 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5777 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5778 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5780 /* fetch the service path */
5781 row = MSI_QueryGetRecord(package->db, query, comp);
5782 if (!row)
5784 ERR("Query failed\n");
5785 goto done;
5787 key = MSI_RecordGetString(row, 6);
5788 file = msi_get_loaded_file(package, key);
5789 msiobj_release(&row->hdr);
5790 if (!file)
5792 ERR("Failed to load the service file\n");
5793 goto done;
5796 if (!args || !args[0]) image_path = file->TargetPath;
5797 else
5799 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5800 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5802 ret = ERROR_OUTOFMEMORY;
5803 goto done;
5806 strcpyW(image_path, file->TargetPath);
5807 strcatW(image_path, szSpace);
5808 strcatW(image_path, args);
5810 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5811 start_type, err_control, image_path, load_order,
5812 NULL, depends, serv_name, pass);
5814 if (!service)
5816 if (GetLastError() != ERROR_SERVICE_EXISTS)
5817 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5819 else if (sd.lpDescription)
5821 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5822 WARN("failed to set service description %u\n", GetLastError());
5825 if (image_path != file->TargetPath) msi_free(image_path);
5826 done:
5827 CloseServiceHandle(service);
5828 CloseServiceHandle(hscm);
5829 msi_free(name);
5830 msi_free(disp);
5831 msi_free(sd.lpDescription);
5832 msi_free(load_order);
5833 msi_free(serv_name);
5834 msi_free(pass);
5835 msi_free(depends);
5836 msi_free(args);
5838 return ret;
5841 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5843 static const WCHAR query[] = {
5844 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5845 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5846 MSIQUERY *view;
5847 UINT rc;
5849 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5850 if (rc != ERROR_SUCCESS)
5851 return ERROR_SUCCESS;
5853 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5854 msiobj_release(&view->hdr);
5855 return rc;
5858 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5859 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5861 LPCWSTR *vector, *temp_vector;
5862 LPWSTR p, q;
5863 DWORD sep_len;
5865 static const WCHAR separator[] = {'[','~',']',0};
5867 *numargs = 0;
5868 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5870 if (!args)
5871 return NULL;
5873 vector = msi_alloc(sizeof(LPWSTR));
5874 if (!vector)
5875 return NULL;
5877 p = args;
5880 (*numargs)++;
5881 vector[*numargs - 1] = p;
5883 if ((q = strstrW(p, separator)))
5885 *q = '\0';
5887 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5888 if (!temp_vector)
5890 msi_free(vector);
5891 return NULL;
5893 vector = temp_vector;
5895 p = q + sep_len;
5897 } while (q);
5899 return vector;
5902 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5904 MSIPACKAGE *package = param;
5905 MSICOMPONENT *comp;
5906 MSIRECORD *uirow;
5907 SC_HANDLE scm = NULL, service = NULL;
5908 LPCWSTR component, *vector = NULL;
5909 LPWSTR name, args, display_name = NULL;
5910 DWORD event, numargs, len, wait, dummy;
5911 UINT r = ERROR_FUNCTION_FAILED;
5912 SERVICE_STATUS_PROCESS status;
5913 ULONGLONG start_time;
5915 component = MSI_RecordGetString(rec, 6);
5916 comp = msi_get_loaded_component(package, component);
5917 if (!comp)
5918 return ERROR_SUCCESS;
5920 comp->Action = msi_get_component_action( package, comp );
5921 if (comp->Action != INSTALLSTATE_LOCAL)
5923 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5924 return ERROR_SUCCESS;
5927 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5928 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5929 event = MSI_RecordGetInteger(rec, 3);
5930 wait = MSI_RecordGetInteger(rec, 5);
5932 if (!(event & msidbServiceControlEventStart))
5934 r = ERROR_SUCCESS;
5935 goto done;
5938 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5939 if (!scm)
5941 ERR("Failed to open the service control manager\n");
5942 goto done;
5945 len = 0;
5946 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5947 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5949 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5950 GetServiceDisplayNameW( scm, name, display_name, &len );
5953 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5954 if (!service)
5956 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5957 goto done;
5960 vector = msi_service_args_to_vector(args, &numargs);
5962 if (!StartServiceW(service, numargs, vector) &&
5963 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5965 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5966 goto done;
5969 r = ERROR_SUCCESS;
5970 if (wait)
5972 /* wait for at most 30 seconds for the service to be up and running */
5973 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5974 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5976 TRACE("failed to query service status (%u)\n", GetLastError());
5977 goto done;
5979 start_time = GetTickCount64();
5980 while (status.dwCurrentState == SERVICE_START_PENDING)
5982 if (GetTickCount64() - start_time > 30000) break;
5983 Sleep(1000);
5984 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5985 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5987 TRACE("failed to query service status (%u)\n", GetLastError());
5988 goto done;
5991 if (status.dwCurrentState != SERVICE_RUNNING)
5993 WARN("service failed to start %u\n", status.dwCurrentState);
5994 r = ERROR_FUNCTION_FAILED;
5998 done:
5999 uirow = MSI_CreateRecord( 2 );
6000 MSI_RecordSetStringW( uirow, 1, display_name );
6001 MSI_RecordSetStringW( uirow, 2, name );
6002 msi_ui_actiondata( package, szStartServices, uirow );
6003 msiobj_release( &uirow->hdr );
6005 CloseServiceHandle(service);
6006 CloseServiceHandle(scm);
6008 msi_free(name);
6009 msi_free(args);
6010 msi_free(vector);
6011 msi_free(display_name);
6012 return r;
6015 static UINT ACTION_StartServices( MSIPACKAGE *package )
6017 static const WCHAR query[] = {
6018 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6019 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6020 MSIQUERY *view;
6021 UINT rc;
6023 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6024 if (rc != ERROR_SUCCESS)
6025 return ERROR_SUCCESS;
6027 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6028 msiobj_release(&view->hdr);
6029 return rc;
6032 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6034 DWORD i, needed, count;
6035 ENUM_SERVICE_STATUSW *dependencies;
6036 SERVICE_STATUS ss;
6037 SC_HANDLE depserv;
6038 BOOL stopped, ret = FALSE;
6040 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6041 0, &needed, &count))
6042 return TRUE;
6044 if (GetLastError() != ERROR_MORE_DATA)
6045 return FALSE;
6047 dependencies = msi_alloc(needed);
6048 if (!dependencies)
6049 return FALSE;
6051 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6052 needed, &needed, &count))
6053 goto done;
6055 for (i = 0; i < count; i++)
6057 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6058 SERVICE_STOP | SERVICE_QUERY_STATUS);
6059 if (!depserv)
6060 goto done;
6062 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6063 CloseServiceHandle(depserv);
6064 if (!stopped)
6065 goto done;
6068 ret = TRUE;
6070 done:
6071 msi_free(dependencies);
6072 return ret;
6075 static UINT stop_service( LPCWSTR name )
6077 SC_HANDLE scm = NULL, service = NULL;
6078 SERVICE_STATUS status;
6079 SERVICE_STATUS_PROCESS ssp;
6080 DWORD needed;
6082 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6083 if (!scm)
6085 WARN("Failed to open the SCM: %d\n", GetLastError());
6086 goto done;
6089 service = OpenServiceW(scm, name,
6090 SERVICE_STOP |
6091 SERVICE_QUERY_STATUS |
6092 SERVICE_ENUMERATE_DEPENDENTS);
6093 if (!service)
6095 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6096 goto done;
6099 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6100 sizeof(SERVICE_STATUS_PROCESS), &needed))
6102 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6103 goto done;
6106 if (ssp.dwCurrentState == SERVICE_STOPPED)
6107 goto done;
6109 stop_service_dependents(scm, service);
6111 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6112 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6114 done:
6115 CloseServiceHandle(service);
6116 CloseServiceHandle(scm);
6118 return ERROR_SUCCESS;
6121 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6123 MSIPACKAGE *package = param;
6124 MSICOMPONENT *comp;
6125 MSIRECORD *uirow;
6126 LPCWSTR component;
6127 LPWSTR name = NULL, display_name = NULL;
6128 DWORD event, len;
6129 SC_HANDLE scm;
6131 event = MSI_RecordGetInteger( rec, 3 );
6132 if (!(event & msidbServiceControlEventStop))
6133 return ERROR_SUCCESS;
6135 component = MSI_RecordGetString( rec, 6 );
6136 comp = msi_get_loaded_component( package, component );
6137 if (!comp)
6138 return ERROR_SUCCESS;
6140 comp->Action = msi_get_component_action( package, comp );
6141 if (comp->Action != INSTALLSTATE_ABSENT)
6143 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6144 return ERROR_SUCCESS;
6147 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6148 if (!scm)
6150 ERR("Failed to open the service control manager\n");
6151 goto done;
6154 len = 0;
6155 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6156 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6158 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6159 GetServiceDisplayNameW( scm, name, display_name, &len );
6161 CloseServiceHandle( scm );
6163 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6164 stop_service( name );
6166 done:
6167 uirow = MSI_CreateRecord( 2 );
6168 MSI_RecordSetStringW( uirow, 1, display_name );
6169 MSI_RecordSetStringW( uirow, 2, name );
6170 msi_ui_actiondata( package, szStopServices, uirow );
6171 msiobj_release( &uirow->hdr );
6173 msi_free( name );
6174 msi_free( display_name );
6175 return ERROR_SUCCESS;
6178 static UINT ACTION_StopServices( MSIPACKAGE *package )
6180 static const WCHAR query[] = {
6181 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6182 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6183 MSIQUERY *view;
6184 UINT rc;
6186 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6187 if (rc != ERROR_SUCCESS)
6188 return ERROR_SUCCESS;
6190 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6191 msiobj_release(&view->hdr);
6192 return rc;
6195 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6197 MSIPACKAGE *package = param;
6198 MSICOMPONENT *comp;
6199 MSIRECORD *uirow;
6200 LPWSTR name = NULL, display_name = NULL;
6201 DWORD event, len;
6202 SC_HANDLE scm = NULL, service = NULL;
6204 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6205 if (!comp)
6206 return ERROR_SUCCESS;
6208 event = MSI_RecordGetInteger( rec, 3 );
6209 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6211 comp->Action = msi_get_component_action( package, comp );
6212 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6213 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6215 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6216 msi_free( name );
6217 return ERROR_SUCCESS;
6219 stop_service( name );
6221 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6222 if (!scm)
6224 WARN("Failed to open the SCM: %d\n", GetLastError());
6225 goto done;
6228 len = 0;
6229 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6230 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6232 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6233 GetServiceDisplayNameW( scm, name, display_name, &len );
6236 service = OpenServiceW( scm, name, DELETE );
6237 if (!service)
6239 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6240 goto done;
6243 if (!DeleteService( service ))
6244 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6246 done:
6247 uirow = MSI_CreateRecord( 2 );
6248 MSI_RecordSetStringW( uirow, 1, display_name );
6249 MSI_RecordSetStringW( uirow, 2, name );
6250 msi_ui_actiondata( package, szDeleteServices, uirow );
6251 msiobj_release( &uirow->hdr );
6253 CloseServiceHandle( service );
6254 CloseServiceHandle( scm );
6255 msi_free( name );
6256 msi_free( display_name );
6258 return ERROR_SUCCESS;
6261 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6263 static const WCHAR query[] = {
6264 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6265 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6266 MSIQUERY *view;
6267 UINT rc;
6269 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6270 if (rc != ERROR_SUCCESS)
6271 return ERROR_SUCCESS;
6273 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6274 msiobj_release( &view->hdr );
6275 return rc;
6278 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6280 MSIPACKAGE *package = param;
6281 LPWSTR driver, driver_path, ptr;
6282 WCHAR outpath[MAX_PATH];
6283 MSIFILE *driver_file = NULL, *setup_file = NULL;
6284 MSICOMPONENT *comp;
6285 MSIRECORD *uirow;
6286 LPCWSTR desc, file_key, component;
6287 DWORD len, usage;
6288 UINT r = ERROR_SUCCESS;
6290 static const WCHAR driver_fmt[] = {
6291 'D','r','i','v','e','r','=','%','s',0};
6292 static const WCHAR setup_fmt[] = {
6293 'S','e','t','u','p','=','%','s',0};
6294 static const WCHAR usage_fmt[] = {
6295 'F','i','l','e','U','s','a','g','e','=','1',0};
6297 component = MSI_RecordGetString( rec, 2 );
6298 comp = msi_get_loaded_component( package, component );
6299 if (!comp)
6300 return ERROR_SUCCESS;
6302 comp->Action = msi_get_component_action( package, comp );
6303 if (comp->Action != INSTALLSTATE_LOCAL)
6305 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6306 return ERROR_SUCCESS;
6308 desc = MSI_RecordGetString(rec, 3);
6310 file_key = MSI_RecordGetString( rec, 4 );
6311 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6313 file_key = MSI_RecordGetString( rec, 5 );
6314 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6316 if (!driver_file)
6318 ERR("ODBC Driver entry not found!\n");
6319 return ERROR_FUNCTION_FAILED;
6322 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6323 if (setup_file)
6324 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6325 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6327 driver = msi_alloc(len * sizeof(WCHAR));
6328 if (!driver)
6329 return ERROR_OUTOFMEMORY;
6331 ptr = driver;
6332 lstrcpyW(ptr, desc);
6333 ptr += lstrlenW(ptr) + 1;
6335 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6336 ptr += len + 1;
6338 if (setup_file)
6340 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6341 ptr += len + 1;
6344 lstrcpyW(ptr, usage_fmt);
6345 ptr += lstrlenW(ptr) + 1;
6346 *ptr = '\0';
6348 if (!driver_file->TargetPath)
6350 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6351 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6353 driver_path = strdupW(driver_file->TargetPath);
6354 ptr = strrchrW(driver_path, '\\');
6355 if (ptr) *ptr = '\0';
6357 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6358 NULL, ODBC_INSTALL_COMPLETE, &usage))
6360 ERR("Failed to install SQL driver!\n");
6361 r = ERROR_FUNCTION_FAILED;
6364 uirow = MSI_CreateRecord( 5 );
6365 MSI_RecordSetStringW( uirow, 1, desc );
6366 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6367 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6368 msi_ui_actiondata( package, szInstallODBC, uirow );
6369 msiobj_release( &uirow->hdr );
6371 msi_free(driver);
6372 msi_free(driver_path);
6374 return r;
6377 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6379 MSIPACKAGE *package = param;
6380 LPWSTR translator, translator_path, ptr;
6381 WCHAR outpath[MAX_PATH];
6382 MSIFILE *translator_file = NULL, *setup_file = NULL;
6383 MSICOMPONENT *comp;
6384 MSIRECORD *uirow;
6385 LPCWSTR desc, file_key, component;
6386 DWORD len, usage;
6387 UINT r = ERROR_SUCCESS;
6389 static const WCHAR translator_fmt[] = {
6390 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6391 static const WCHAR setup_fmt[] = {
6392 'S','e','t','u','p','=','%','s',0};
6394 component = MSI_RecordGetString( rec, 2 );
6395 comp = msi_get_loaded_component( package, component );
6396 if (!comp)
6397 return ERROR_SUCCESS;
6399 comp->Action = msi_get_component_action( package, comp );
6400 if (comp->Action != INSTALLSTATE_LOCAL)
6402 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6403 return ERROR_SUCCESS;
6405 desc = MSI_RecordGetString(rec, 3);
6407 file_key = MSI_RecordGetString( rec, 4 );
6408 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6410 file_key = MSI_RecordGetString( rec, 5 );
6411 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6413 if (!translator_file)
6415 ERR("ODBC Translator entry not found!\n");
6416 return ERROR_FUNCTION_FAILED;
6419 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6420 if (setup_file)
6421 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6423 translator = msi_alloc(len * sizeof(WCHAR));
6424 if (!translator)
6425 return ERROR_OUTOFMEMORY;
6427 ptr = translator;
6428 lstrcpyW(ptr, desc);
6429 ptr += lstrlenW(ptr) + 1;
6431 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6432 ptr += len + 1;
6434 if (setup_file)
6436 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6437 ptr += len + 1;
6439 *ptr = '\0';
6441 translator_path = strdupW(translator_file->TargetPath);
6442 ptr = strrchrW(translator_path, '\\');
6443 if (ptr) *ptr = '\0';
6445 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6446 NULL, ODBC_INSTALL_COMPLETE, &usage))
6448 ERR("Failed to install SQL translator!\n");
6449 r = ERROR_FUNCTION_FAILED;
6452 uirow = MSI_CreateRecord( 5 );
6453 MSI_RecordSetStringW( uirow, 1, desc );
6454 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6455 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6456 msi_ui_actiondata( package, szInstallODBC, uirow );
6457 msiobj_release( &uirow->hdr );
6459 msi_free(translator);
6460 msi_free(translator_path);
6462 return r;
6465 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6467 MSIPACKAGE *package = param;
6468 MSICOMPONENT *comp;
6469 LPWSTR attrs;
6470 LPCWSTR desc, driver, component;
6471 WORD request = ODBC_ADD_SYS_DSN;
6472 INT registration;
6473 DWORD len;
6474 UINT r = ERROR_SUCCESS;
6475 MSIRECORD *uirow;
6477 static const WCHAR attrs_fmt[] = {
6478 'D','S','N','=','%','s',0 };
6480 component = MSI_RecordGetString( rec, 2 );
6481 comp = msi_get_loaded_component( package, component );
6482 if (!comp)
6483 return ERROR_SUCCESS;
6485 comp->Action = msi_get_component_action( package, comp );
6486 if (comp->Action != INSTALLSTATE_LOCAL)
6488 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6489 return ERROR_SUCCESS;
6492 desc = MSI_RecordGetString(rec, 3);
6493 driver = MSI_RecordGetString(rec, 4);
6494 registration = MSI_RecordGetInteger(rec, 5);
6496 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6497 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6499 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6500 attrs = msi_alloc(len * sizeof(WCHAR));
6501 if (!attrs)
6502 return ERROR_OUTOFMEMORY;
6504 len = sprintfW(attrs, attrs_fmt, desc);
6505 attrs[len + 1] = 0;
6507 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6509 ERR("Failed to install SQL data source!\n");
6510 r = ERROR_FUNCTION_FAILED;
6513 uirow = MSI_CreateRecord( 5 );
6514 MSI_RecordSetStringW( uirow, 1, desc );
6515 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6516 MSI_RecordSetInteger( uirow, 3, request );
6517 msi_ui_actiondata( package, szInstallODBC, uirow );
6518 msiobj_release( &uirow->hdr );
6520 msi_free(attrs);
6522 return r;
6525 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6527 static const WCHAR driver_query[] = {
6528 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6529 'O','D','B','C','D','r','i','v','e','r',0};
6530 static const WCHAR translator_query[] = {
6531 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6532 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6533 static const WCHAR source_query[] = {
6534 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6535 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6536 MSIQUERY *view;
6537 UINT rc;
6539 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6540 if (rc == ERROR_SUCCESS)
6542 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6543 msiobj_release(&view->hdr);
6544 if (rc != ERROR_SUCCESS)
6545 return rc;
6547 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6548 if (rc == ERROR_SUCCESS)
6550 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6551 msiobj_release(&view->hdr);
6552 if (rc != ERROR_SUCCESS)
6553 return rc;
6555 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6556 if (rc == ERROR_SUCCESS)
6558 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6559 msiobj_release(&view->hdr);
6560 if (rc != ERROR_SUCCESS)
6561 return rc;
6563 return ERROR_SUCCESS;
6566 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6568 MSIPACKAGE *package = param;
6569 MSICOMPONENT *comp;
6570 MSIRECORD *uirow;
6571 DWORD usage;
6572 LPCWSTR desc, component;
6574 component = MSI_RecordGetString( rec, 2 );
6575 comp = msi_get_loaded_component( package, component );
6576 if (!comp)
6577 return ERROR_SUCCESS;
6579 comp->Action = msi_get_component_action( package, comp );
6580 if (comp->Action != INSTALLSTATE_ABSENT)
6582 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6583 return ERROR_SUCCESS;
6586 desc = MSI_RecordGetString( rec, 3 );
6587 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6589 WARN("Failed to remove ODBC driver\n");
6591 else if (!usage)
6593 FIXME("Usage count reached 0\n");
6596 uirow = MSI_CreateRecord( 2 );
6597 MSI_RecordSetStringW( uirow, 1, desc );
6598 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6599 msi_ui_actiondata( package, szRemoveODBC, uirow );
6600 msiobj_release( &uirow->hdr );
6602 return ERROR_SUCCESS;
6605 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6607 MSIPACKAGE *package = param;
6608 MSICOMPONENT *comp;
6609 MSIRECORD *uirow;
6610 DWORD usage;
6611 LPCWSTR desc, component;
6613 component = MSI_RecordGetString( rec, 2 );
6614 comp = msi_get_loaded_component( package, component );
6615 if (!comp)
6616 return ERROR_SUCCESS;
6618 comp->Action = msi_get_component_action( package, comp );
6619 if (comp->Action != INSTALLSTATE_ABSENT)
6621 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6622 return ERROR_SUCCESS;
6625 desc = MSI_RecordGetString( rec, 3 );
6626 if (!SQLRemoveTranslatorW( desc, &usage ))
6628 WARN("Failed to remove ODBC translator\n");
6630 else if (!usage)
6632 FIXME("Usage count reached 0\n");
6635 uirow = MSI_CreateRecord( 2 );
6636 MSI_RecordSetStringW( uirow, 1, desc );
6637 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6638 msi_ui_actiondata( package, szRemoveODBC, uirow );
6639 msiobj_release( &uirow->hdr );
6641 return ERROR_SUCCESS;
6644 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6646 MSIPACKAGE *package = param;
6647 MSICOMPONENT *comp;
6648 MSIRECORD *uirow;
6649 LPWSTR attrs;
6650 LPCWSTR desc, driver, component;
6651 WORD request = ODBC_REMOVE_SYS_DSN;
6652 INT registration;
6653 DWORD len;
6655 static const WCHAR attrs_fmt[] = {
6656 'D','S','N','=','%','s',0 };
6658 component = MSI_RecordGetString( rec, 2 );
6659 comp = msi_get_loaded_component( package, component );
6660 if (!comp)
6661 return ERROR_SUCCESS;
6663 comp->Action = msi_get_component_action( package, comp );
6664 if (comp->Action != INSTALLSTATE_ABSENT)
6666 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6667 return ERROR_SUCCESS;
6670 desc = MSI_RecordGetString( rec, 3 );
6671 driver = MSI_RecordGetString( rec, 4 );
6672 registration = MSI_RecordGetInteger( rec, 5 );
6674 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6675 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6677 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6678 attrs = msi_alloc( len * sizeof(WCHAR) );
6679 if (!attrs)
6680 return ERROR_OUTOFMEMORY;
6682 FIXME("Use ODBCSourceAttribute table\n");
6684 len = sprintfW( attrs, attrs_fmt, desc );
6685 attrs[len + 1] = 0;
6687 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6689 WARN("Failed to remove ODBC data source\n");
6691 msi_free( attrs );
6693 uirow = MSI_CreateRecord( 3 );
6694 MSI_RecordSetStringW( uirow, 1, desc );
6695 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6696 MSI_RecordSetInteger( uirow, 3, request );
6697 msi_ui_actiondata( package, szRemoveODBC, uirow );
6698 msiobj_release( &uirow->hdr );
6700 return ERROR_SUCCESS;
6703 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6705 static const WCHAR driver_query[] = {
6706 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6707 'O','D','B','C','D','r','i','v','e','r',0};
6708 static const WCHAR translator_query[] = {
6709 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6710 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6711 static const WCHAR source_query[] = {
6712 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6713 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6714 MSIQUERY *view;
6715 UINT rc;
6717 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6718 if (rc == ERROR_SUCCESS)
6720 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6721 msiobj_release( &view->hdr );
6722 if (rc != ERROR_SUCCESS)
6723 return rc;
6725 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6726 if (rc == ERROR_SUCCESS)
6728 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6729 msiobj_release( &view->hdr );
6730 if (rc != ERROR_SUCCESS)
6731 return rc;
6733 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6734 if (rc == ERROR_SUCCESS)
6736 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6737 msiobj_release( &view->hdr );
6738 if (rc != ERROR_SUCCESS)
6739 return rc;
6741 return ERROR_SUCCESS;
6744 #define ENV_ACT_SETALWAYS 0x1
6745 #define ENV_ACT_SETABSENT 0x2
6746 #define ENV_ACT_REMOVE 0x4
6747 #define ENV_ACT_REMOVEMATCH 0x8
6749 #define ENV_MOD_MACHINE 0x20000000
6750 #define ENV_MOD_APPEND 0x40000000
6751 #define ENV_MOD_PREFIX 0x80000000
6752 #define ENV_MOD_MASK 0xC0000000
6754 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6756 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6758 LPCWSTR cptr = *name;
6760 static const WCHAR prefix[] = {'[','~',']',0};
6761 static const int prefix_len = 3;
6763 *flags = 0;
6764 while (*cptr)
6766 if (*cptr == '=')
6767 *flags |= ENV_ACT_SETALWAYS;
6768 else if (*cptr == '+')
6769 *flags |= ENV_ACT_SETABSENT;
6770 else if (*cptr == '-')
6771 *flags |= ENV_ACT_REMOVE;
6772 else if (*cptr == '!')
6773 *flags |= ENV_ACT_REMOVEMATCH;
6774 else if (*cptr == '*')
6775 *flags |= ENV_MOD_MACHINE;
6776 else
6777 break;
6779 cptr++;
6780 (*name)++;
6783 if (!*cptr)
6785 ERR("Missing environment variable\n");
6786 return ERROR_FUNCTION_FAILED;
6789 if (*value)
6791 LPCWSTR ptr = *value;
6792 if (!strncmpW(ptr, prefix, prefix_len))
6794 if (ptr[prefix_len] == szSemiColon[0])
6796 *flags |= ENV_MOD_APPEND;
6797 *value += lstrlenW(prefix);
6799 else
6801 *value = NULL;
6804 else if (lstrlenW(*value) >= prefix_len)
6806 ptr += lstrlenW(ptr) - prefix_len;
6807 if (!strcmpW( ptr, prefix ))
6809 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6811 *flags |= ENV_MOD_PREFIX;
6812 /* the "[~]" will be removed by deformat_string */;
6814 else
6816 *value = NULL;
6822 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6823 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6824 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6825 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6827 ERR("Invalid flags: %08x\n", *flags);
6828 return ERROR_FUNCTION_FAILED;
6831 if (!*flags)
6832 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6834 return ERROR_SUCCESS;
6837 static UINT open_env_key( DWORD flags, HKEY *key )
6839 static const WCHAR user_env[] =
6840 {'E','n','v','i','r','o','n','m','e','n','t',0};
6841 static const WCHAR machine_env[] =
6842 {'S','y','s','t','e','m','\\',
6843 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6844 'C','o','n','t','r','o','l','\\',
6845 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6846 'E','n','v','i','r','o','n','m','e','n','t',0};
6847 const WCHAR *env;
6848 HKEY root;
6849 LONG res;
6851 if (flags & ENV_MOD_MACHINE)
6853 env = machine_env;
6854 root = HKEY_LOCAL_MACHINE;
6856 else
6858 env = user_env;
6859 root = HKEY_CURRENT_USER;
6862 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6863 if (res != ERROR_SUCCESS)
6865 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6866 return ERROR_FUNCTION_FAILED;
6869 return ERROR_SUCCESS;
6872 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6874 MSIPACKAGE *package = param;
6875 LPCWSTR name, value, component;
6876 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6877 DWORD flags, type, size;
6878 UINT res;
6879 HKEY env = NULL;
6880 MSICOMPONENT *comp;
6881 MSIRECORD *uirow;
6882 int action = 0;
6884 component = MSI_RecordGetString(rec, 4);
6885 comp = msi_get_loaded_component(package, component);
6886 if (!comp)
6887 return ERROR_SUCCESS;
6889 comp->Action = msi_get_component_action( package, comp );
6890 if (comp->Action != INSTALLSTATE_LOCAL)
6892 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6893 return ERROR_SUCCESS;
6895 name = MSI_RecordGetString(rec, 2);
6896 value = MSI_RecordGetString(rec, 3);
6898 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6900 res = env_parse_flags(&name, &value, &flags);
6901 if (res != ERROR_SUCCESS || !value)
6902 goto done;
6904 if (value && !deformat_string(package, value, &deformatted))
6906 res = ERROR_OUTOFMEMORY;
6907 goto done;
6910 value = deformatted;
6912 res = open_env_key( flags, &env );
6913 if (res != ERROR_SUCCESS)
6914 goto done;
6916 if (flags & ENV_MOD_MACHINE)
6917 action |= 0x20000000;
6919 size = 0;
6920 type = REG_SZ;
6921 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6922 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6923 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6924 goto done;
6926 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6928 action = 0x2;
6930 /* Nothing to do. */
6931 if (!value)
6933 res = ERROR_SUCCESS;
6934 goto done;
6937 /* If we are appending but the string was empty, strip ; */
6938 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6940 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6941 newval = strdupW(value);
6942 if (!newval)
6944 res = ERROR_OUTOFMEMORY;
6945 goto done;
6948 else
6950 action = 0x1;
6952 /* Contrary to MSDN, +-variable to [~];path works */
6953 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6955 res = ERROR_SUCCESS;
6956 goto done;
6959 data = msi_alloc(size);
6960 if (!data)
6962 RegCloseKey(env);
6963 return ERROR_OUTOFMEMORY;
6966 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6967 if (res != ERROR_SUCCESS)
6968 goto done;
6970 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6972 action = 0x4;
6973 res = RegDeleteValueW(env, name);
6974 if (res != ERROR_SUCCESS)
6975 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6976 goto done;
6979 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6980 if (flags & ENV_MOD_MASK)
6982 DWORD mod_size;
6983 int multiplier = 0;
6984 if (flags & ENV_MOD_APPEND) multiplier++;
6985 if (flags & ENV_MOD_PREFIX) multiplier++;
6986 mod_size = lstrlenW(value) * multiplier;
6987 size += mod_size * sizeof(WCHAR);
6990 newval = msi_alloc(size);
6991 ptr = newval;
6992 if (!newval)
6994 res = ERROR_OUTOFMEMORY;
6995 goto done;
6998 if (flags & ENV_MOD_PREFIX)
7000 lstrcpyW(newval, value);
7001 ptr = newval + lstrlenW(value);
7002 action |= 0x80000000;
7005 lstrcpyW(ptr, data);
7007 if (flags & ENV_MOD_APPEND)
7009 lstrcatW(newval, value);
7010 action |= 0x40000000;
7013 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7014 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7015 if (res)
7017 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7020 done:
7021 uirow = MSI_CreateRecord( 3 );
7022 MSI_RecordSetStringW( uirow, 1, name );
7023 MSI_RecordSetStringW( uirow, 2, newval );
7024 MSI_RecordSetInteger( uirow, 3, action );
7025 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7026 msiobj_release( &uirow->hdr );
7028 if (env) RegCloseKey(env);
7029 msi_free(deformatted);
7030 msi_free(data);
7031 msi_free(newval);
7032 return res;
7035 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7037 static const WCHAR query[] = {
7038 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7039 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7040 MSIQUERY *view;
7041 UINT rc;
7043 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7044 if (rc != ERROR_SUCCESS)
7045 return ERROR_SUCCESS;
7047 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7048 msiobj_release(&view->hdr);
7049 return rc;
7052 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7054 MSIPACKAGE *package = param;
7055 LPCWSTR name, value, component;
7056 LPWSTR deformatted = NULL;
7057 DWORD flags;
7058 HKEY env;
7059 MSICOMPONENT *comp;
7060 MSIRECORD *uirow;
7061 int action = 0;
7062 LONG res;
7063 UINT r;
7065 component = MSI_RecordGetString( rec, 4 );
7066 comp = msi_get_loaded_component( package, component );
7067 if (!comp)
7068 return ERROR_SUCCESS;
7070 comp->Action = msi_get_component_action( package, comp );
7071 if (comp->Action != INSTALLSTATE_ABSENT)
7073 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7074 return ERROR_SUCCESS;
7076 name = MSI_RecordGetString( rec, 2 );
7077 value = MSI_RecordGetString( rec, 3 );
7079 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7081 r = env_parse_flags( &name, &value, &flags );
7082 if (r != ERROR_SUCCESS)
7083 return r;
7085 if (!(flags & ENV_ACT_REMOVE))
7087 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7088 return ERROR_SUCCESS;
7091 if (value && !deformat_string( package, value, &deformatted ))
7092 return ERROR_OUTOFMEMORY;
7094 value = deformatted;
7096 r = open_env_key( flags, &env );
7097 if (r != ERROR_SUCCESS)
7099 r = ERROR_SUCCESS;
7100 goto done;
7103 if (flags & ENV_MOD_MACHINE)
7104 action |= 0x20000000;
7106 TRACE("Removing %s\n", debugstr_w(name));
7108 res = RegDeleteValueW( env, name );
7109 if (res != ERROR_SUCCESS)
7111 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7112 r = ERROR_SUCCESS;
7115 done:
7116 uirow = MSI_CreateRecord( 3 );
7117 MSI_RecordSetStringW( uirow, 1, name );
7118 MSI_RecordSetStringW( uirow, 2, value );
7119 MSI_RecordSetInteger( uirow, 3, action );
7120 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7121 msiobj_release( &uirow->hdr );
7123 if (env) RegCloseKey( env );
7124 msi_free( deformatted );
7125 return r;
7128 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7130 static const WCHAR query[] = {
7131 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7132 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7133 MSIQUERY *view;
7134 UINT rc;
7136 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7137 if (rc != ERROR_SUCCESS)
7138 return ERROR_SUCCESS;
7140 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7141 msiobj_release( &view->hdr );
7142 return rc;
7145 UINT msi_validate_product_id( MSIPACKAGE *package )
7147 LPWSTR key, template, id;
7148 UINT r = ERROR_SUCCESS;
7150 id = msi_dup_property( package->db, szProductID );
7151 if (id)
7153 msi_free( id );
7154 return ERROR_SUCCESS;
7156 template = msi_dup_property( package->db, szPIDTemplate );
7157 key = msi_dup_property( package->db, szPIDKEY );
7158 if (key && template)
7160 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7161 r = msi_set_property( package->db, szProductID, key, -1 );
7163 msi_free( template );
7164 msi_free( key );
7165 return r;
7168 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7170 return msi_validate_product_id( package );
7173 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7175 TRACE("\n");
7176 package->need_reboot_at_end = 1;
7177 return ERROR_SUCCESS;
7180 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7182 static const WCHAR szAvailableFreeReg[] =
7183 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7184 MSIRECORD *uirow;
7185 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7187 TRACE("%p %d kilobytes\n", package, space);
7189 uirow = MSI_CreateRecord( 1 );
7190 MSI_RecordSetInteger( uirow, 1, space );
7191 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7192 msiobj_release( &uirow->hdr );
7194 return ERROR_SUCCESS;
7197 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7199 TRACE("%p\n", package);
7201 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7202 return ERROR_SUCCESS;
7205 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7207 FIXME("%p\n", package);
7208 return ERROR_SUCCESS;
7211 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7213 static const WCHAR driver_query[] = {
7214 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7215 'O','D','B','C','D','r','i','v','e','r',0};
7216 static const WCHAR translator_query[] = {
7217 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7218 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7219 MSIQUERY *view;
7220 UINT r, count;
7222 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7223 if (r == ERROR_SUCCESS)
7225 count = 0;
7226 r = MSI_IterateRecords( view, &count, NULL, package );
7227 msiobj_release( &view->hdr );
7228 if (r != ERROR_SUCCESS)
7229 return r;
7230 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7232 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7233 if (r == ERROR_SUCCESS)
7235 count = 0;
7236 r = MSI_IterateRecords( view, &count, NULL, package );
7237 msiobj_release( &view->hdr );
7238 if (r != ERROR_SUCCESS)
7239 return r;
7240 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7242 return ERROR_SUCCESS;
7245 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7247 static const WCHAR fmtW[] =
7248 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7249 MSIPACKAGE *package = param;
7250 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7251 int attrs = MSI_RecordGetInteger( rec, 5 );
7252 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7253 WCHAR *product, *features, *cmd;
7254 STARTUPINFOW si;
7255 PROCESS_INFORMATION info;
7256 BOOL ret;
7258 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7259 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7261 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7263 len += strlenW( product );
7264 if (features)
7265 len += strlenW( features );
7266 else
7267 len += sizeof(szAll) / sizeof(szAll[0]);
7269 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7271 msi_free( product );
7272 msi_free( features );
7273 return ERROR_OUTOFMEMORY;
7275 sprintfW( cmd, fmtW, product, features ? features : szAll );
7276 msi_free( product );
7277 msi_free( features );
7279 memset( &si, 0, sizeof(STARTUPINFOW) );
7280 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7281 msi_free( cmd );
7282 if (!ret) return GetLastError();
7283 CloseHandle( info.hThread );
7285 WaitForSingleObject( info.hProcess, INFINITE );
7286 CloseHandle( info.hProcess );
7287 return ERROR_SUCCESS;
7290 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7292 static const WCHAR query[] = {
7293 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7294 MSIQUERY *view;
7295 UINT r;
7297 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7298 if (r == ERROR_SUCCESS)
7300 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7301 msiobj_release( &view->hdr );
7302 if (r != ERROR_SUCCESS)
7303 return r;
7305 return ERROR_SUCCESS;
7308 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7310 MSIPACKAGE *package = param;
7311 int attributes = MSI_RecordGetInteger( rec, 5 );
7313 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7315 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7316 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7317 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7318 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7319 HKEY hkey;
7320 UINT r;
7322 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7324 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7325 if (r != ERROR_SUCCESS)
7326 return ERROR_SUCCESS;
7328 else
7330 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7331 if (r != ERROR_SUCCESS)
7332 return ERROR_SUCCESS;
7334 RegCloseKey( hkey );
7336 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7337 debugstr_w(upgrade_code), debugstr_w(version_min),
7338 debugstr_w(version_max), debugstr_w(language));
7340 return ERROR_SUCCESS;
7343 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7345 static const WCHAR query[] = {
7346 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7347 'U','p','g','r','a','d','e',0};
7348 MSIQUERY *view;
7349 UINT r;
7351 if (msi_get_property_int( package->db, szInstalled, 0 ))
7353 TRACE("product is installed, skipping action\n");
7354 return ERROR_SUCCESS;
7356 if (msi_get_property_int( package->db, szPreselected, 0 ))
7358 TRACE("Preselected property is set, not migrating feature states\n");
7359 return ERROR_SUCCESS;
7361 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7362 if (r == ERROR_SUCCESS)
7364 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7365 msiobj_release( &view->hdr );
7366 if (r != ERROR_SUCCESS)
7367 return r;
7369 return ERROR_SUCCESS;
7372 static void bind_image( const char *filename, const char *path )
7374 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7376 WARN("failed to bind image %u\n", GetLastError());
7380 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7382 UINT i;
7383 MSIFILE *file;
7384 MSIPACKAGE *package = param;
7385 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7386 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7387 char *filenameA, *pathA;
7388 WCHAR *pathW, **path_list;
7390 if (!(file = msi_get_loaded_file( package, key )))
7392 WARN("file %s not found\n", debugstr_w(key));
7393 return ERROR_SUCCESS;
7395 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7396 path_list = msi_split_string( paths, ';' );
7397 if (!path_list) bind_image( filenameA, NULL );
7398 else
7400 for (i = 0; path_list[i] && path_list[i][0]; i++)
7402 deformat_string( package, path_list[i], &pathW );
7403 if ((pathA = strdupWtoA( pathW )))
7405 bind_image( filenameA, pathA );
7406 msi_free( pathA );
7408 msi_free( pathW );
7411 msi_free( path_list );
7412 msi_free( filenameA );
7413 return ERROR_SUCCESS;
7416 static UINT ACTION_BindImage( MSIPACKAGE *package )
7418 static const WCHAR query[] = {
7419 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7420 'B','i','n','d','I','m','a','g','e',0};
7421 MSIQUERY *view;
7422 UINT r;
7424 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7425 if (r == ERROR_SUCCESS)
7427 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7428 msiobj_release( &view->hdr );
7429 if (r != ERROR_SUCCESS)
7430 return r;
7432 return ERROR_SUCCESS;
7435 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7437 static const WCHAR query[] = {
7438 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7439 MSIQUERY *view;
7440 DWORD count = 0;
7441 UINT r;
7443 r = MSI_OpenQuery( package->db, &view, query, table );
7444 if (r == ERROR_SUCCESS)
7446 r = MSI_IterateRecords(view, &count, NULL, package);
7447 msiobj_release(&view->hdr);
7448 if (r != ERROR_SUCCESS)
7449 return r;
7451 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7452 return ERROR_SUCCESS;
7455 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7457 static const WCHAR table[] = {
7458 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7459 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7462 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7464 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7465 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7468 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7470 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7471 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7474 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7476 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7477 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7480 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7482 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7483 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7486 static const struct
7488 const WCHAR *action;
7489 UINT (*handler)(MSIPACKAGE *);
7490 const WCHAR *action_rollback;
7492 StandardActions[] =
7494 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7495 { szAppSearch, ACTION_AppSearch, NULL },
7496 { szBindImage, ACTION_BindImage, NULL },
7497 { szCCPSearch, ACTION_CCPSearch, NULL },
7498 { szCostFinalize, ACTION_CostFinalize, NULL },
7499 { szCostInitialize, ACTION_CostInitialize, NULL },
7500 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7501 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7502 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7503 { szDisableRollback, ACTION_DisableRollback, NULL },
7504 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7505 { szExecuteAction, ACTION_ExecuteAction, NULL },
7506 { szFileCost, ACTION_FileCost, NULL },
7507 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7508 { szForceReboot, ACTION_ForceReboot, NULL },
7509 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7510 { szInstallExecute, ACTION_InstallExecute, NULL },
7511 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7512 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7513 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7514 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7515 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7516 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7517 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7518 { szInstallValidate, ACTION_InstallValidate, NULL },
7519 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7520 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7521 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7522 { szMoveFiles, ACTION_MoveFiles, NULL },
7523 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7524 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7525 { szPatchFiles, ACTION_PatchFiles, NULL },
7526 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7527 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7528 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7529 { szPublishProduct, ACTION_PublishProduct, NULL },
7530 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7531 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7532 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7533 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7534 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7535 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7536 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7537 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7538 { szRegisterUser, ACTION_RegisterUser, NULL },
7539 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7540 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7541 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7542 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7543 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7544 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7545 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7546 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7547 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7548 { szResolveSource, ACTION_ResolveSource, NULL },
7549 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7550 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7551 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7552 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7553 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7554 { szStartServices, ACTION_StartServices, szStopServices },
7555 { szStopServices, ACTION_StopServices, szStartServices },
7556 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7557 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7558 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7559 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7560 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7561 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7562 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7563 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7564 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7565 { szValidateProductID, ACTION_ValidateProductID, NULL },
7566 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7567 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7568 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7569 { NULL, NULL, NULL }
7572 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7574 BOOL ret = FALSE;
7575 UINT i;
7577 i = 0;
7578 while (StandardActions[i].action != NULL)
7580 if (!strcmpW( StandardActions[i].action, action ))
7582 ui_actionstart( package, action );
7583 if (StandardActions[i].handler)
7585 ui_actioninfo( package, action, TRUE, 0 );
7586 *rc = StandardActions[i].handler( package );
7587 ui_actioninfo( package, action, FALSE, *rc );
7589 if (StandardActions[i].action_rollback && !package->need_rollback)
7591 TRACE("scheduling rollback action\n");
7592 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7595 else
7597 FIXME("unhandled standard action %s\n", debugstr_w(action));
7598 *rc = ERROR_SUCCESS;
7600 ret = TRUE;
7601 break;
7603 i++;
7605 return ret;
7608 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7610 UINT rc = ERROR_SUCCESS;
7611 BOOL handled;
7613 TRACE("Performing action (%s)\n", debugstr_w(action));
7615 handled = ACTION_HandleStandardAction(package, action, &rc);
7617 if (!handled)
7618 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7620 if (!handled)
7622 WARN("unhandled msi action %s\n", debugstr_w(action));
7623 rc = ERROR_FUNCTION_NOT_CALLED;
7626 return rc;
7629 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7631 UINT rc = ERROR_SUCCESS;
7632 BOOL handled = FALSE;
7634 TRACE("Performing action (%s)\n", debugstr_w(action));
7636 package->action_progress_increment = 0;
7637 handled = ACTION_HandleStandardAction(package, action, &rc);
7639 if (!handled)
7640 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7642 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7643 handled = TRUE;
7645 if (!handled)
7647 WARN("unhandled msi action %s\n", debugstr_w(action));
7648 rc = ERROR_FUNCTION_NOT_CALLED;
7651 return rc;
7654 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7656 UINT rc = ERROR_SUCCESS;
7657 MSIRECORD *row;
7659 static const WCHAR query[] =
7660 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7661 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7662 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7663 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7664 static const WCHAR ui_query[] =
7665 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7666 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7667 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7668 ' ', '=',' ','%','i',0};
7670 if (needs_ui_sequence(package))
7671 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7672 else
7673 row = MSI_QueryGetRecord(package->db, query, seq);
7675 if (row)
7677 LPCWSTR action, cond;
7679 TRACE("Running the actions\n");
7681 /* check conditions */
7682 cond = MSI_RecordGetString(row, 2);
7684 /* this is a hack to skip errors in the condition code */
7685 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7687 msiobj_release(&row->hdr);
7688 return ERROR_SUCCESS;
7691 action = MSI_RecordGetString(row, 1);
7692 if (!action)
7694 ERR("failed to fetch action\n");
7695 msiobj_release(&row->hdr);
7696 return ERROR_FUNCTION_FAILED;
7699 if (needs_ui_sequence(package))
7700 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7701 else
7702 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7704 msiobj_release(&row->hdr);
7707 return rc;
7710 /****************************************************
7711 * TOP level entry points
7712 *****************************************************/
7714 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7715 LPCWSTR szCommandLine )
7717 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7718 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7719 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7720 WCHAR *reinstall, *remove, *patch;
7721 BOOL ui_exists;
7722 UINT rc;
7724 msi_set_property( package->db, szAction, szInstall, -1 );
7726 package->script->InWhatSequence = SEQUENCE_INSTALL;
7728 if (szPackagePath)
7730 LPWSTR p, dir;
7731 LPCWSTR file;
7733 dir = strdupW(szPackagePath);
7734 p = strrchrW(dir, '\\');
7735 if (p)
7737 *(++p) = 0;
7738 file = szPackagePath + (p - dir);
7740 else
7742 msi_free(dir);
7743 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7744 GetCurrentDirectoryW(MAX_PATH, dir);
7745 lstrcatW(dir, szBackSlash);
7746 file = szPackagePath;
7749 msi_free( package->PackagePath );
7750 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7751 if (!package->PackagePath)
7753 msi_free(dir);
7754 return ERROR_OUTOFMEMORY;
7757 lstrcpyW(package->PackagePath, dir);
7758 lstrcatW(package->PackagePath, file);
7759 msi_free(dir);
7761 msi_set_sourcedir_props(package, FALSE);
7764 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7765 if (rc != ERROR_SUCCESS)
7766 return rc;
7768 msi_apply_transforms( package );
7769 msi_apply_patches( package );
7771 patch = msi_dup_property( package->db, szPatch );
7772 remove = msi_dup_property( package->db, szRemove );
7773 reinstall = msi_dup_property( package->db, szReinstall );
7774 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7776 TRACE("setting REINSTALL property to ALL\n");
7777 msi_set_property( package->db, szReinstall, szAll, -1 );
7778 package->full_reinstall = 1;
7781 /* properties may have been added by a transform */
7782 msi_clone_properties( package );
7783 msi_set_original_database_property( package->db, szPackagePath );
7785 msi_parse_command_line( package, szCommandLine, FALSE );
7786 msi_adjust_privilege_properties( package );
7787 msi_set_context( package );
7789 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7791 TRACE("disabling rollback\n");
7792 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7795 if (needs_ui_sequence( package))
7797 package->script->InWhatSequence |= SEQUENCE_UI;
7798 rc = ACTION_ProcessUISequence(package);
7799 ui_exists = ui_sequence_exists(package);
7800 if (rc == ERROR_SUCCESS || !ui_exists)
7802 package->script->InWhatSequence |= SEQUENCE_EXEC;
7803 rc = ACTION_ProcessExecSequence(package, ui_exists);
7806 else
7807 rc = ACTION_ProcessExecSequence(package, FALSE);
7809 package->script->CurrentlyScripting = FALSE;
7811 /* process the ending type action */
7812 if (rc == ERROR_SUCCESS)
7813 ACTION_PerformActionSequence(package, -1);
7814 else if (rc == ERROR_INSTALL_USEREXIT)
7815 ACTION_PerformActionSequence(package, -2);
7816 else if (rc == ERROR_INSTALL_SUSPEND)
7817 ACTION_PerformActionSequence(package, -4);
7818 else /* failed */
7820 ACTION_PerformActionSequence(package, -3);
7821 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7823 package->need_rollback = TRUE;
7827 /* finish up running custom actions */
7828 ACTION_FinishCustomActions(package);
7830 if (package->need_rollback && !reinstall)
7832 WARN("installation failed, running rollback script\n");
7833 execute_script( package, SCRIPT_ROLLBACK );
7835 msi_free( reinstall );
7836 msi_free( remove );
7837 msi_free( patch );
7839 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7840 return ERROR_SUCCESS_REBOOT_REQUIRED;
7842 return rc;