shlwapi: Use proper helpers for iface calls.
[wine/multimedia.git] / dlls / msi / action.c
blobf8745285cf7ab0112b28027f7f8e2c4abf8c41b1
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 in_quotes = 1;
226 ignore = 1;
227 len++;
228 break;
229 case '"':
230 state = state_quote;
231 if (in_quotes && p[1] != '\"') count--;
232 else count++;
233 break;
234 default:
235 state = state_token;
236 in_quotes = 1;
237 len++;
238 break;
240 break;
242 case state_token:
243 switch (*p)
245 case '"':
246 state = state_quote;
247 if (in_quotes) count--;
248 else count++;
249 break;
250 case ' ':
251 state = state_whitespace;
252 if (!count) goto done;
253 in_quotes = 1;
254 len++;
255 break;
256 default:
257 if (!count) in_quotes = 0;
258 else in_quotes = 1;
259 len++;
260 break;
262 break;
264 case state_quote:
265 switch (*p)
267 case '"':
268 if (in_quotes && p[1] != '\"') count--;
269 else count++;
270 break;
271 case ' ':
272 state = state_whitespace;
273 if (!count || (count > 1 && !len)) goto done;
274 in_quotes = 1;
275 len++;
276 break;
277 default:
278 state = state_token;
279 if (!count) in_quotes = 0;
280 else in_quotes = 1;
281 len++;
282 break;
284 break;
286 default: break;
288 if (!ignore) *out++ = *p;
291 done:
292 if (!len) *value = 0;
293 else *out = 0;
295 *quotes = count;
296 return p - str;
299 static void remove_quotes( WCHAR *str )
301 WCHAR *p = str;
302 int len = strlenW( str );
304 while ((p = strchrW( p, '"' )))
306 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
307 p++;
311 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
312 BOOL preserve_case )
314 LPCWSTR ptr, ptr2;
315 int num_quotes;
316 DWORD len;
317 WCHAR *prop, *val;
318 UINT r;
320 if (!szCommandLine)
321 return ERROR_SUCCESS;
323 ptr = szCommandLine;
324 while (*ptr)
326 while (*ptr == ' ') ptr++;
327 if (!*ptr) break;
329 ptr2 = strchrW( ptr, '=' );
330 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
332 len = ptr2 - ptr;
333 if (!len) return ERROR_INVALID_COMMAND_LINE;
335 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
336 memcpy( prop, ptr, len * sizeof(WCHAR) );
337 prop[len] = 0;
338 if (!preserve_case) struprW( prop );
340 ptr2++;
341 while (*ptr2 == ' ') ptr2++;
343 num_quotes = 0;
344 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
345 len = parse_prop( ptr2, val, &num_quotes );
346 if (num_quotes % 2)
348 WARN("unbalanced quotes\n");
349 msi_free( val );
350 msi_free( prop );
351 return ERROR_INVALID_COMMAND_LINE;
353 remove_quotes( val );
354 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
356 r = msi_set_property( package->db, prop, val );
357 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
358 msi_reset_folders( package, TRUE );
360 msi_free( val );
361 msi_free( prop );
363 ptr = ptr2 + len;
366 return ERROR_SUCCESS;
369 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
371 LPCWSTR pc;
372 LPWSTR p, *ret = NULL;
373 UINT count = 0;
375 if (!str)
376 return ret;
378 /* count the number of substrings */
379 for ( pc = str, count = 0; pc; count++ )
381 pc = strchrW( pc, sep );
382 if (pc)
383 pc++;
386 /* allocate space for an array of substring pointers and the substrings */
387 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
388 (lstrlenW(str)+1) * sizeof(WCHAR) );
389 if (!ret)
390 return ret;
392 /* copy the string and set the pointers */
393 p = (LPWSTR) &ret[count+1];
394 lstrcpyW( p, str );
395 for( count = 0; (ret[count] = p); count++ )
397 p = strchrW( p, sep );
398 if (p)
399 *p++ = 0;
402 return ret;
405 static BOOL ui_sequence_exists( MSIPACKAGE *package )
407 static const WCHAR query [] = {
408 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
409 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
410 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
411 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
412 MSIQUERY *view;
413 UINT rc;
415 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
416 if (rc == ERROR_SUCCESS)
418 msiobj_release(&view->hdr);
419 return TRUE;
421 return FALSE;
424 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
426 LPWSTR source, check;
428 if (msi_get_property_int( package->db, szInstalled, 0 ))
430 HKEY hkey;
432 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
433 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
434 RegCloseKey( hkey );
436 else
438 LPWSTR p, db;
439 DWORD len;
441 db = msi_dup_property( package->db, szOriginalDatabase );
442 if (!db)
443 return ERROR_OUTOFMEMORY;
445 p = strrchrW( db, '\\' );
446 if (!p)
448 p = strrchrW( db, '/' );
449 if (!p)
451 msi_free(db);
452 return ERROR_SUCCESS;
456 len = p - db + 2;
457 source = msi_alloc( len * sizeof(WCHAR) );
458 lstrcpynW( source, db, len );
459 msi_free( db );
462 check = msi_dup_property( package->db, szSourceDir );
463 if (!check || replace)
465 UINT r = msi_set_property( package->db, szSourceDir, source );
466 if (r == ERROR_SUCCESS)
467 msi_reset_folders( package, TRUE );
469 msi_free( check );
471 check = msi_dup_property( package->db, szSOURCEDIR );
472 if (!check || replace)
473 msi_set_property( package->db, szSOURCEDIR, source );
475 msi_free( check );
476 msi_free( source );
478 return ERROR_SUCCESS;
481 static BOOL needs_ui_sequence(MSIPACKAGE *package)
483 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
486 UINT msi_set_context(MSIPACKAGE *package)
488 UINT r = msi_locate_product( package->ProductCode, &package->Context );
489 if (r != ERROR_SUCCESS)
491 int num = msi_get_property_int( package->db, szAllUsers, 0 );
492 if (num == 1 || num == 2)
493 package->Context = MSIINSTALLCONTEXT_MACHINE;
494 else
495 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
497 return ERROR_SUCCESS;
500 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
502 UINT rc;
503 LPCWSTR cond, action;
504 MSIPACKAGE *package = param;
506 action = MSI_RecordGetString(row,1);
507 if (!action)
509 ERR("Error is retrieving action name\n");
510 return ERROR_FUNCTION_FAILED;
513 /* check conditions */
514 cond = MSI_RecordGetString(row,2);
516 /* this is a hack to skip errors in the condition code */
517 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
519 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
520 return ERROR_SUCCESS;
523 if (needs_ui_sequence(package))
524 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
525 else
526 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
528 msi_dialog_check_messages( NULL );
530 if (package->CurrentInstallState != ERROR_SUCCESS)
531 rc = package->CurrentInstallState;
533 if (rc == ERROR_FUNCTION_NOT_CALLED)
534 rc = ERROR_SUCCESS;
536 if (rc != ERROR_SUCCESS)
537 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
539 if (package->need_reboot_now)
541 TRACE("action %s asked for immediate reboot, suspending installation\n",
542 debugstr_w(action));
543 rc = ACTION_ForceReboot( package );
545 return rc;
548 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
550 static const WCHAR query[] = {
551 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
552 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
553 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
554 '`','S','e','q','u','e','n','c','e','`',0};
555 MSIQUERY *view;
556 UINT r;
558 TRACE("%p %s\n", package, debugstr_w(table));
560 r = MSI_OpenQuery( package->db, &view, query, table );
561 if (r == ERROR_SUCCESS)
563 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
564 msiobj_release(&view->hdr);
566 return r;
569 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
571 static const WCHAR query[] = {
572 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
573 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
574 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
575 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
576 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
577 static const WCHAR query_validate[] = {
578 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
579 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
580 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
581 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
582 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
583 MSIQUERY *view;
584 INT seq = 0;
585 UINT rc;
587 if (package->script->ExecuteSequenceRun)
589 TRACE("Execute Sequence already Run\n");
590 return ERROR_SUCCESS;
593 package->script->ExecuteSequenceRun = TRUE;
595 /* get the sequence number */
596 if (UIran)
598 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
599 if (!row) return ERROR_FUNCTION_FAILED;
600 seq = MSI_RecordGetInteger(row,1);
601 msiobj_release(&row->hdr);
603 rc = MSI_OpenQuery(package->db, &view, query, seq);
604 if (rc == ERROR_SUCCESS)
606 TRACE("Running the actions\n");
608 msi_set_property(package->db, szSourceDir, NULL);
609 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
610 msiobj_release(&view->hdr);
612 return rc;
615 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
617 static const WCHAR query[] = {
618 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
619 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
620 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
621 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
622 MSIQUERY *view;
623 UINT rc;
625 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
626 if (rc == ERROR_SUCCESS)
628 TRACE("Running the actions\n");
629 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
630 msiobj_release(&view->hdr);
632 return rc;
635 /********************************************************
636 * ACTION helper functions and functions that perform the actions
637 *******************************************************/
638 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
639 UINT* rc, UINT script, BOOL force )
641 BOOL ret=FALSE;
642 UINT arc;
644 arc = ACTION_CustomAction(package, action, script, force);
646 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
648 *rc = arc;
649 ret = TRUE;
651 return ret;
654 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
656 MSICOMPONENT *comp;
658 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
660 if (!strcmpW( Component, comp->Component )) return comp;
662 return NULL;
665 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
667 MSIFEATURE *feature;
669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
671 if (!strcmpW( Feature, feature->Feature )) return feature;
673 return NULL;
676 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
678 MSIFILE *file;
680 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
682 if (!strcmpW( key, file->File )) return file;
684 return NULL;
687 MSIFILEPATCH *msi_get_loaded_filepatch( MSIPACKAGE *package, const WCHAR *key )
689 MSIFILEPATCH *patch;
691 /* FIXME: There might be more than one patch */
692 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
694 if (!strcmpW( key, patch->File->File )) return patch;
696 return NULL;
699 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
701 MSIFOLDER *folder;
703 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
705 if (!strcmpW( dir, folder->Directory )) return folder;
707 return NULL;
711 * Recursively create all directories in the path.
712 * shamelessly stolen from setupapi/queue.c
714 BOOL msi_create_full_path( const WCHAR *path )
716 BOOL ret = TRUE;
717 WCHAR *new_path;
718 int len;
720 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
721 strcpyW( new_path, path );
723 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
724 new_path[len - 1] = 0;
726 while (!CreateDirectoryW( new_path, NULL ))
728 WCHAR *slash;
729 DWORD last_error = GetLastError();
730 if (last_error == ERROR_ALREADY_EXISTS) break;
731 if (last_error != ERROR_PATH_NOT_FOUND)
733 ret = FALSE;
734 break;
736 if (!(slash = strrchrW( new_path, '\\' )))
738 ret = FALSE;
739 break;
741 len = slash - new_path;
742 new_path[len] = 0;
743 if (!msi_create_full_path( new_path ))
745 ret = FALSE;
746 break;
748 new_path[len] = '\\';
750 msi_free( new_path );
751 return ret;
754 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
756 MSIRECORD *row;
758 row = MSI_CreateRecord( 4 );
759 MSI_RecordSetInteger( row, 1, a );
760 MSI_RecordSetInteger( row, 2, b );
761 MSI_RecordSetInteger( row, 3, c );
762 MSI_RecordSetInteger( row, 4, d );
763 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
764 msiobj_release( &row->hdr );
766 msi_dialog_check_messages( NULL );
769 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
771 static const WCHAR query[] =
772 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
773 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
774 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
775 WCHAR message[1024];
776 MSIRECORD *row = 0;
777 DWORD size;
779 if (!package->LastAction || strcmpW( package->LastAction, action ))
781 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
783 if (MSI_RecordIsNull( row, 3 ))
785 msiobj_release( &row->hdr );
786 return;
788 /* update the cached action format */
789 msi_free( package->ActionFormat );
790 package->ActionFormat = msi_dup_record_field( row, 3 );
791 msi_free( package->LastAction );
792 package->LastAction = strdupW( action );
793 msiobj_release( &row->hdr );
795 size = 1024;
796 MSI_RecordSetStringW( record, 0, package->ActionFormat );
797 MSI_FormatRecordW( package, record, message, &size );
798 row = MSI_CreateRecord( 1 );
799 MSI_RecordSetStringW( row, 1, message );
800 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
801 msiobj_release( &row->hdr );
804 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
806 if (!comp->Enabled)
808 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
809 return INSTALLSTATE_UNKNOWN;
811 if (package->need_rollback) return comp->Installed;
812 return comp->ActionRequest;
815 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
817 if (package->need_rollback) return feature->Installed;
818 return feature->ActionRequest;
821 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
823 MSIPACKAGE *package = param;
824 LPCWSTR dir, component, full_path;
825 MSIRECORD *uirow;
826 MSIFOLDER *folder;
827 MSICOMPONENT *comp;
829 component = MSI_RecordGetString(row, 2);
830 if (!component)
831 return ERROR_SUCCESS;
833 comp = msi_get_loaded_component(package, component);
834 if (!comp)
835 return ERROR_SUCCESS;
837 comp->Action = msi_get_component_action( package, comp );
838 if (comp->Action != INSTALLSTATE_LOCAL)
840 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
841 return ERROR_SUCCESS;
844 dir = MSI_RecordGetString(row,1);
845 if (!dir)
847 ERR("Unable to get folder id\n");
848 return ERROR_SUCCESS;
851 uirow = MSI_CreateRecord(1);
852 MSI_RecordSetStringW(uirow, 1, dir);
853 msi_ui_actiondata(package, szCreateFolders, uirow);
854 msiobj_release(&uirow->hdr);
856 full_path = msi_get_target_folder( package, dir );
857 if (!full_path)
859 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
860 return ERROR_SUCCESS;
862 TRACE("folder is %s\n", debugstr_w(full_path));
864 folder = msi_get_loaded_folder( package, dir );
865 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
866 folder->State = FOLDER_STATE_CREATED;
867 return ERROR_SUCCESS;
870 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
872 static const WCHAR query[] = {
873 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
874 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
875 MSIQUERY *view;
876 UINT rc;
878 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
879 if (rc != ERROR_SUCCESS)
880 return ERROR_SUCCESS;
882 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
883 msiobj_release(&view->hdr);
884 return rc;
887 static void remove_persistent_folder( MSIFOLDER *folder )
889 FolderList *fl;
891 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
893 remove_persistent_folder( fl->folder );
895 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
897 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
901 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
903 MSIPACKAGE *package = param;
904 LPCWSTR dir, component, full_path;
905 MSIRECORD *uirow;
906 MSIFOLDER *folder;
907 MSICOMPONENT *comp;
909 component = MSI_RecordGetString(row, 2);
910 if (!component)
911 return ERROR_SUCCESS;
913 comp = msi_get_loaded_component(package, component);
914 if (!comp)
915 return ERROR_SUCCESS;
917 comp->Action = msi_get_component_action( package, comp );
918 if (comp->Action != INSTALLSTATE_ABSENT)
920 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
921 return ERROR_SUCCESS;
924 dir = MSI_RecordGetString( row, 1 );
925 if (!dir)
927 ERR("Unable to get folder id\n");
928 return ERROR_SUCCESS;
931 full_path = msi_get_target_folder( package, dir );
932 if (!full_path)
934 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
935 return ERROR_SUCCESS;
937 TRACE("folder is %s\n", debugstr_w(full_path));
939 uirow = MSI_CreateRecord( 1 );
940 MSI_RecordSetStringW( uirow, 1, dir );
941 msi_ui_actiondata( package, szRemoveFolders, uirow );
942 msiobj_release( &uirow->hdr );
944 folder = msi_get_loaded_folder( package, dir );
945 remove_persistent_folder( folder );
946 return ERROR_SUCCESS;
949 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
951 static const WCHAR query[] = {
952 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
953 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
954 MSIQUERY *view;
955 UINT rc;
957 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
958 if (rc != ERROR_SUCCESS)
959 return ERROR_SUCCESS;
961 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
962 msiobj_release( &view->hdr );
963 return rc;
966 static UINT load_component( MSIRECORD *row, LPVOID param )
968 MSIPACKAGE *package = param;
969 MSICOMPONENT *comp;
971 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
972 if (!comp)
973 return ERROR_FUNCTION_FAILED;
975 list_add_tail( &package->components, &comp->entry );
977 /* fill in the data */
978 comp->Component = msi_dup_record_field( row, 1 );
980 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
982 comp->ComponentId = msi_dup_record_field( row, 2 );
983 comp->Directory = msi_dup_record_field( row, 3 );
984 comp->Attributes = MSI_RecordGetInteger(row,4);
985 comp->Condition = msi_dup_record_field( row, 5 );
986 comp->KeyPath = msi_dup_record_field( row, 6 );
988 comp->Installed = INSTALLSTATE_UNKNOWN;
989 comp->Action = INSTALLSTATE_UNKNOWN;
990 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
992 comp->assembly = msi_load_assembly( package, comp );
993 return ERROR_SUCCESS;
996 UINT msi_load_all_components( MSIPACKAGE *package )
998 static const WCHAR query[] = {
999 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1000 '`','C','o','m','p','o','n','e','n','t','`',0};
1001 MSIQUERY *view;
1002 UINT r;
1004 if (!list_empty(&package->components))
1005 return ERROR_SUCCESS;
1007 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1008 if (r != ERROR_SUCCESS)
1009 return r;
1011 if (!msi_init_assembly_caches( package ))
1013 ERR("can't initialize assembly caches\n");
1014 msiobj_release( &view->hdr );
1015 return ERROR_FUNCTION_FAILED;
1018 r = MSI_IterateRecords(view, NULL, load_component, package);
1019 msiobj_release(&view->hdr);
1020 return r;
1023 typedef struct {
1024 MSIPACKAGE *package;
1025 MSIFEATURE *feature;
1026 } _ilfs;
1028 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1030 ComponentList *cl;
1032 cl = msi_alloc( sizeof (*cl) );
1033 if ( !cl )
1034 return ERROR_NOT_ENOUGH_MEMORY;
1035 cl->component = comp;
1036 list_add_tail( &feature->Components, &cl->entry );
1038 return ERROR_SUCCESS;
1041 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1043 FeatureList *fl;
1045 fl = msi_alloc( sizeof(*fl) );
1046 if ( !fl )
1047 return ERROR_NOT_ENOUGH_MEMORY;
1048 fl->feature = child;
1049 list_add_tail( &parent->Children, &fl->entry );
1051 return ERROR_SUCCESS;
1054 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1056 _ilfs* ilfs = param;
1057 LPCWSTR component;
1058 MSICOMPONENT *comp;
1060 component = MSI_RecordGetString(row,1);
1062 /* check to see if the component is already loaded */
1063 comp = msi_get_loaded_component( ilfs->package, component );
1064 if (!comp)
1066 WARN("ignoring unknown component %s\n", debugstr_w(component));
1067 return ERROR_SUCCESS;
1069 add_feature_component( ilfs->feature, comp );
1070 comp->Enabled = TRUE;
1072 return ERROR_SUCCESS;
1075 static UINT load_feature(MSIRECORD * row, LPVOID param)
1077 static const WCHAR query[] = {
1078 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1079 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1080 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1081 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1082 MSIPACKAGE *package = param;
1083 MSIFEATURE *feature;
1084 MSIQUERY *view;
1085 _ilfs ilfs;
1086 UINT rc;
1088 /* fill in the data */
1090 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1091 if (!feature)
1092 return ERROR_NOT_ENOUGH_MEMORY;
1094 list_init( &feature->Children );
1095 list_init( &feature->Components );
1097 feature->Feature = msi_dup_record_field( row, 1 );
1099 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1101 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1102 feature->Title = msi_dup_record_field( row, 3 );
1103 feature->Description = msi_dup_record_field( row, 4 );
1105 if (!MSI_RecordIsNull(row,5))
1106 feature->Display = MSI_RecordGetInteger(row,5);
1108 feature->Level= MSI_RecordGetInteger(row,6);
1109 feature->Directory = msi_dup_record_field( row, 7 );
1110 feature->Attributes = MSI_RecordGetInteger(row,8);
1112 feature->Installed = INSTALLSTATE_UNKNOWN;
1113 feature->Action = INSTALLSTATE_UNKNOWN;
1114 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1116 list_add_tail( &package->features, &feature->entry );
1118 /* load feature components */
1120 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1121 if (rc != ERROR_SUCCESS)
1122 return ERROR_SUCCESS;
1124 ilfs.package = package;
1125 ilfs.feature = feature;
1127 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1128 msiobj_release(&view->hdr);
1129 return rc;
1132 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1134 MSIPACKAGE *package = param;
1135 MSIFEATURE *parent, *child;
1137 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1138 if (!child)
1139 return ERROR_FUNCTION_FAILED;
1141 if (!child->Feature_Parent)
1142 return ERROR_SUCCESS;
1144 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1145 if (!parent)
1146 return ERROR_FUNCTION_FAILED;
1148 add_feature_child( parent, child );
1149 return ERROR_SUCCESS;
1152 UINT msi_load_all_features( MSIPACKAGE *package )
1154 static const WCHAR query[] = {
1155 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1156 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1157 '`','D','i','s','p','l','a','y','`',0};
1158 MSIQUERY *view;
1159 UINT r;
1161 if (!list_empty(&package->features))
1162 return ERROR_SUCCESS;
1164 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1165 if (r != ERROR_SUCCESS)
1166 return r;
1168 r = MSI_IterateRecords( view, NULL, load_feature, package );
1169 if (r != ERROR_SUCCESS)
1171 msiobj_release( &view->hdr );
1172 return r;
1174 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1175 msiobj_release( &view->hdr );
1176 return r;
1179 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1181 if (!p)
1182 return p;
1183 p = strchrW(p, ch);
1184 if (!p)
1185 return p;
1186 *p = 0;
1187 return p+1;
1190 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1192 static const WCHAR query[] = {
1193 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1194 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1195 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1196 MSIQUERY *view = NULL;
1197 MSIRECORD *row = NULL;
1198 UINT r;
1200 TRACE("%s\n", debugstr_w(file->File));
1202 r = MSI_OpenQuery(package->db, &view, query, file->File);
1203 if (r != ERROR_SUCCESS)
1204 goto done;
1206 r = MSI_ViewExecute(view, NULL);
1207 if (r != ERROR_SUCCESS)
1208 goto done;
1210 r = MSI_ViewFetch(view, &row);
1211 if (r != ERROR_SUCCESS)
1212 goto done;
1214 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1215 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1216 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1217 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1218 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1220 done:
1221 if (view) msiobj_release(&view->hdr);
1222 if (row) msiobj_release(&row->hdr);
1223 return r;
1226 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1228 MSIRECORD *row;
1229 static const WCHAR query[] = {
1230 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1231 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1232 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1234 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1235 if (!row)
1237 WARN("query failed\n");
1238 return ERROR_FUNCTION_FAILED;
1241 file->disk_id = MSI_RecordGetInteger( row, 1 );
1242 msiobj_release( &row->hdr );
1243 return ERROR_SUCCESS;
1246 static UINT load_file(MSIRECORD *row, LPVOID param)
1248 MSIPACKAGE* package = param;
1249 LPCWSTR component;
1250 MSIFILE *file;
1252 /* fill in the data */
1254 file = msi_alloc_zero( sizeof (MSIFILE) );
1255 if (!file)
1256 return ERROR_NOT_ENOUGH_MEMORY;
1258 file->File = msi_dup_record_field( row, 1 );
1260 component = MSI_RecordGetString( row, 2 );
1261 file->Component = msi_get_loaded_component( package, component );
1263 if (!file->Component)
1265 WARN("Component not found: %s\n", debugstr_w(component));
1266 msi_free(file->File);
1267 msi_free(file);
1268 return ERROR_SUCCESS;
1271 file->FileName = msi_dup_record_field( row, 3 );
1272 msi_reduce_to_long_filename( file->FileName );
1274 file->ShortName = msi_dup_record_field( row, 3 );
1275 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1277 file->FileSize = MSI_RecordGetInteger( row, 4 );
1278 file->Version = msi_dup_record_field( row, 5 );
1279 file->Language = msi_dup_record_field( row, 6 );
1280 file->Attributes = MSI_RecordGetInteger( row, 7 );
1281 file->Sequence = MSI_RecordGetInteger( row, 8 );
1283 file->state = msifs_invalid;
1285 /* if the compressed bits are not set in the file attributes,
1286 * then read the information from the package word count property
1288 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1290 file->IsCompressed = FALSE;
1292 else if (file->Attributes &
1293 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1295 file->IsCompressed = TRUE;
1297 else if (file->Attributes & msidbFileAttributesNoncompressed)
1299 file->IsCompressed = FALSE;
1301 else
1303 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1306 load_file_hash(package, file);
1307 load_file_disk_id(package, file);
1309 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1311 list_add_tail( &package->files, &file->entry );
1313 return ERROR_SUCCESS;
1316 static UINT load_all_files(MSIPACKAGE *package)
1318 static const WCHAR query[] = {
1319 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1320 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1321 '`','S','e','q','u','e','n','c','e','`', 0};
1322 MSIQUERY *view;
1323 UINT rc;
1325 if (!list_empty(&package->files))
1326 return ERROR_SUCCESS;
1328 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1329 if (rc != ERROR_SUCCESS)
1330 return ERROR_SUCCESS;
1332 rc = MSI_IterateRecords(view, NULL, load_file, package);
1333 msiobj_release(&view->hdr);
1334 return rc;
1337 static UINT load_media( MSIRECORD *row, LPVOID param )
1339 MSIPACKAGE *package = param;
1340 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1341 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1343 /* FIXME: load external cabinets and directory sources too */
1344 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1345 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1346 return ERROR_SUCCESS;
1349 static UINT load_all_media( MSIPACKAGE *package )
1351 static const WCHAR query[] = {
1352 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1353 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1354 '`','D','i','s','k','I','d','`',0};
1355 MSIQUERY *view;
1356 UINT r;
1358 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1359 if (r != ERROR_SUCCESS)
1360 return ERROR_SUCCESS;
1362 r = MSI_IterateRecords( view, NULL, load_media, package );
1363 msiobj_release( &view->hdr );
1364 return r;
1367 static UINT load_patch(MSIRECORD *row, LPVOID param)
1369 MSIPACKAGE *package = param;
1370 MSIFILEPATCH *patch;
1371 LPWSTR file_key;
1373 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1374 if (!patch)
1375 return ERROR_NOT_ENOUGH_MEMORY;
1377 file_key = msi_dup_record_field( row, 1 );
1378 patch->File = msi_get_loaded_file( package, file_key );
1379 msi_free(file_key);
1381 if( !patch->File )
1383 ERR("Failed to find target for patch in File table\n");
1384 msi_free(patch);
1385 return ERROR_FUNCTION_FAILED;
1388 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1390 /* FIXME: The database should be properly transformed */
1391 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1393 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1394 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1395 patch->IsApplied = FALSE;
1397 /* FIXME:
1398 * Header field - for patch validation.
1399 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1402 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1404 list_add_tail( &package->filepatches, &patch->entry );
1406 return ERROR_SUCCESS;
1409 static UINT load_all_patches(MSIPACKAGE *package)
1411 static const WCHAR query[] = {
1412 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1413 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1414 '`','S','e','q','u','e','n','c','e','`',0};
1415 MSIQUERY *view;
1416 UINT rc;
1418 if (!list_empty(&package->filepatches))
1419 return ERROR_SUCCESS;
1421 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1422 if (rc != ERROR_SUCCESS)
1423 return ERROR_SUCCESS;
1425 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1426 msiobj_release(&view->hdr);
1427 return rc;
1430 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1432 static const WCHAR query[] = {
1433 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1434 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1435 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1436 MSIQUERY *view;
1438 folder->persistent = FALSE;
1439 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1441 if (!MSI_ViewExecute( view, NULL ))
1443 MSIRECORD *rec;
1444 if (!MSI_ViewFetch( view, &rec ))
1446 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1447 folder->persistent = TRUE;
1448 msiobj_release( &rec->hdr );
1451 msiobj_release( &view->hdr );
1453 return ERROR_SUCCESS;
1456 static UINT load_folder( MSIRECORD *row, LPVOID param )
1458 MSIPACKAGE *package = param;
1459 static WCHAR szEmpty[] = { 0 };
1460 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1461 MSIFOLDER *folder;
1463 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1464 list_init( &folder->children );
1465 folder->Directory = msi_dup_record_field( row, 1 );
1466 folder->Parent = msi_dup_record_field( row, 2 );
1467 p = msi_dup_record_field(row, 3);
1469 TRACE("%s\n", debugstr_w(folder->Directory));
1471 /* split src and target dir */
1472 tgt_short = p;
1473 src_short = folder_split_path( p, ':' );
1475 /* split the long and short paths */
1476 tgt_long = folder_split_path( tgt_short, '|' );
1477 src_long = folder_split_path( src_short, '|' );
1479 /* check for no-op dirs */
1480 if (tgt_short && !strcmpW( szDot, tgt_short ))
1481 tgt_short = szEmpty;
1482 if (src_short && !strcmpW( szDot, src_short ))
1483 src_short = szEmpty;
1485 if (!tgt_long)
1486 tgt_long = tgt_short;
1488 if (!src_short) {
1489 src_short = tgt_short;
1490 src_long = tgt_long;
1493 if (!src_long)
1494 src_long = src_short;
1496 /* FIXME: use the target short path too */
1497 folder->TargetDefault = strdupW(tgt_long);
1498 folder->SourceShortPath = strdupW(src_short);
1499 folder->SourceLongPath = strdupW(src_long);
1500 msi_free(p);
1502 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1503 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1504 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1506 load_folder_persistence( package, folder );
1508 list_add_tail( &package->folders, &folder->entry );
1509 return ERROR_SUCCESS;
1512 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1514 FolderList *fl;
1516 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1517 fl->folder = child;
1518 list_add_tail( &parent->children, &fl->entry );
1519 return ERROR_SUCCESS;
1522 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1524 MSIPACKAGE *package = param;
1525 MSIFOLDER *parent, *child;
1527 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1528 return ERROR_FUNCTION_FAILED;
1530 if (!child->Parent) return ERROR_SUCCESS;
1532 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1533 return ERROR_FUNCTION_FAILED;
1535 return add_folder_child( parent, child );
1538 static UINT load_all_folders( MSIPACKAGE *package )
1540 static const WCHAR query[] = {
1541 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1542 '`','D','i','r','e','c','t','o','r','y','`',0};
1543 MSIQUERY *view;
1544 UINT r;
1546 if (!list_empty(&package->folders))
1547 return ERROR_SUCCESS;
1549 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1550 if (r != ERROR_SUCCESS)
1551 return r;
1553 r = MSI_IterateRecords( view, NULL, load_folder, package );
1554 if (r != ERROR_SUCCESS)
1556 msiobj_release( &view->hdr );
1557 return r;
1559 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1560 msiobj_release( &view->hdr );
1561 return r;
1564 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1566 msi_set_property( package->db, szCostingComplete, szZero );
1567 msi_set_property( package->db, szRootDrive, szCRoot );
1569 load_all_folders( package );
1570 msi_load_all_components( package );
1571 msi_load_all_features( package );
1572 load_all_files( package );
1573 load_all_patches( package );
1574 load_all_media( package );
1576 return ERROR_SUCCESS;
1579 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1581 const WCHAR *action = package->script->Actions[script][index];
1582 ui_actionstart( package, action );
1583 TRACE("executing %s\n", debugstr_w(action));
1584 return ACTION_PerformAction( package, action, script );
1587 static UINT execute_script( MSIPACKAGE *package, UINT script )
1589 UINT i, rc = ERROR_SUCCESS;
1591 TRACE("executing script %u\n", script);
1593 if (!package->script)
1595 ERR("no script!\n");
1596 return ERROR_FUNCTION_FAILED;
1598 if (script == SCRIPT_ROLLBACK)
1600 for (i = package->script->ActionCount[script]; i > 0; i--)
1602 rc = execute_script_action( package, script, i - 1 );
1603 if (rc != ERROR_SUCCESS) break;
1606 else
1608 for (i = 0; i < package->script->ActionCount[script]; i++)
1610 rc = execute_script_action( package, script, i );
1611 if (rc != ERROR_SUCCESS) break;
1614 msi_free_action_script(package, script);
1615 return rc;
1618 static UINT ACTION_FileCost(MSIPACKAGE *package)
1620 return ERROR_SUCCESS;
1623 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1625 MSICOMPONENT *comp;
1626 UINT r;
1628 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1630 if (!comp->ComponentId) continue;
1632 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1633 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1634 &comp->Installed );
1635 if (r == ERROR_SUCCESS) continue;
1637 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1638 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1639 &comp->Installed );
1640 if (r == ERROR_SUCCESS) continue;
1642 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1643 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1644 &comp->Installed );
1645 if (r == ERROR_SUCCESS) continue;
1647 comp->Installed = INSTALLSTATE_ABSENT;
1651 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1653 MSIFEATURE *feature;
1655 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1657 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1659 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1660 feature->Installed = INSTALLSTATE_ABSENT;
1661 else
1662 feature->Installed = state;
1666 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1668 return (feature->Level > 0 && feature->Level <= level);
1671 static BOOL process_state_property(MSIPACKAGE* package, int level,
1672 LPCWSTR property, INSTALLSTATE state)
1674 LPWSTR override;
1675 MSIFEATURE *feature;
1677 override = msi_dup_property( package->db, property );
1678 if (!override)
1679 return FALSE;
1681 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1683 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1684 continue;
1686 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1688 if (!strcmpiW( override, szAll ))
1690 if (feature->Installed != state)
1692 feature->Action = state;
1693 feature->ActionRequest = state;
1696 else
1698 LPWSTR ptr = override;
1699 LPWSTR ptr2 = strchrW(override,',');
1701 while (ptr)
1703 int len = ptr2 - ptr;
1705 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1706 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1708 if (feature->Installed != state)
1710 feature->Action = state;
1711 feature->ActionRequest = state;
1713 break;
1715 if (ptr2)
1717 ptr=ptr2+1;
1718 ptr2 = strchrW(ptr,',');
1720 else
1721 break;
1725 msi_free(override);
1726 return TRUE;
1729 static BOOL process_overrides( MSIPACKAGE *package, int level )
1731 static const WCHAR szAddLocal[] =
1732 {'A','D','D','L','O','C','A','L',0};
1733 static const WCHAR szAddSource[] =
1734 {'A','D','D','S','O','U','R','C','E',0};
1735 static const WCHAR szAdvertise[] =
1736 {'A','D','V','E','R','T','I','S','E',0};
1737 BOOL ret = FALSE;
1739 /* all these activation/deactivation things happen in order and things
1740 * later on the list override things earlier on the list.
1742 * 0 INSTALLLEVEL processing
1743 * 1 ADDLOCAL
1744 * 2 REMOVE
1745 * 3 ADDSOURCE
1746 * 4 ADDDEFAULT
1747 * 5 REINSTALL
1748 * 6 ADVERTISE
1749 * 7 COMPADDLOCAL
1750 * 8 COMPADDSOURCE
1751 * 9 FILEADDLOCAL
1752 * 10 FILEADDSOURCE
1753 * 11 FILEADDDEFAULT
1755 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1756 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1757 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1758 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1759 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1761 if (ret)
1762 msi_set_property( package->db, szPreselected, szOne );
1764 return ret;
1767 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1769 int level;
1770 MSICOMPONENT* component;
1771 MSIFEATURE *feature;
1773 TRACE("Checking Install Level\n");
1775 level = msi_get_property_int(package->db, szInstallLevel, 1);
1777 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1779 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1781 if (!is_feature_selected( feature, level )) continue;
1783 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1785 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1787 feature->Action = INSTALLSTATE_SOURCE;
1788 feature->ActionRequest = INSTALLSTATE_SOURCE;
1790 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1792 feature->Action = INSTALLSTATE_ADVERTISED;
1793 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1795 else
1797 feature->Action = INSTALLSTATE_LOCAL;
1798 feature->ActionRequest = INSTALLSTATE_LOCAL;
1802 /* disable child features of unselected parent or follow parent */
1803 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1805 FeatureList *fl;
1807 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1809 if (!is_feature_selected( feature, level ))
1811 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1812 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1814 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1816 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1817 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1818 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1819 fl->feature->Action = feature->Action;
1820 fl->feature->ActionRequest = feature->ActionRequest;
1825 else /* preselected */
1827 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1829 if (!is_feature_selected( feature, level )) continue;
1831 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1833 if (feature->Installed == INSTALLSTATE_ABSENT)
1835 feature->Action = INSTALLSTATE_UNKNOWN;
1836 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1838 else
1840 feature->Action = feature->Installed;
1841 feature->ActionRequest = feature->Installed;
1845 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1847 FeatureList *fl;
1849 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1851 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1852 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1854 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1855 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1856 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1857 fl->feature->Action = feature->Action;
1858 fl->feature->ActionRequest = feature->ActionRequest;
1864 /* now we want to set component state based based on feature state */
1865 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1867 ComponentList *cl;
1869 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1870 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1871 feature->ActionRequest, feature->Action);
1873 if (!is_feature_selected( feature, level )) continue;
1875 /* features with components that have compressed files are made local */
1876 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1878 if (cl->component->ForceLocalState &&
1879 feature->ActionRequest == INSTALLSTATE_SOURCE)
1881 feature->Action = INSTALLSTATE_LOCAL;
1882 feature->ActionRequest = INSTALLSTATE_LOCAL;
1883 break;
1887 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1889 component = cl->component;
1891 switch (feature->ActionRequest)
1893 case INSTALLSTATE_ABSENT:
1894 component->anyAbsent = 1;
1895 break;
1896 case INSTALLSTATE_ADVERTISED:
1897 component->hasAdvertiseFeature = 1;
1898 break;
1899 case INSTALLSTATE_SOURCE:
1900 component->hasSourceFeature = 1;
1901 break;
1902 case INSTALLSTATE_LOCAL:
1903 component->hasLocalFeature = 1;
1904 break;
1905 case INSTALLSTATE_DEFAULT:
1906 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1907 component->hasAdvertiseFeature = 1;
1908 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1909 component->hasSourceFeature = 1;
1910 else
1911 component->hasLocalFeature = 1;
1912 break;
1913 default:
1914 break;
1919 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1921 /* check if it's local or source */
1922 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1923 (component->hasLocalFeature || component->hasSourceFeature))
1925 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1926 !component->ForceLocalState)
1928 component->Action = INSTALLSTATE_SOURCE;
1929 component->ActionRequest = INSTALLSTATE_SOURCE;
1931 else
1933 component->Action = INSTALLSTATE_LOCAL;
1934 component->ActionRequest = INSTALLSTATE_LOCAL;
1936 continue;
1939 /* if any feature is local, the component must be local too */
1940 if (component->hasLocalFeature)
1942 component->Action = INSTALLSTATE_LOCAL;
1943 component->ActionRequest = INSTALLSTATE_LOCAL;
1944 continue;
1946 if (component->hasSourceFeature)
1948 component->Action = INSTALLSTATE_SOURCE;
1949 component->ActionRequest = INSTALLSTATE_SOURCE;
1950 continue;
1952 if (component->hasAdvertiseFeature)
1954 component->Action = INSTALLSTATE_ADVERTISED;
1955 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1956 continue;
1958 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1959 if (component->anyAbsent &&
1960 (component->Installed == INSTALLSTATE_LOCAL || component->Installed == INSTALLSTATE_SOURCE))
1962 component->Action = INSTALLSTATE_ABSENT;
1963 component->ActionRequest = INSTALLSTATE_ABSENT;
1967 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1969 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1971 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1972 component->Action = INSTALLSTATE_LOCAL;
1973 component->ActionRequest = INSTALLSTATE_LOCAL;
1976 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1977 component->Installed == INSTALLSTATE_SOURCE &&
1978 component->hasSourceFeature)
1980 component->Action = INSTALLSTATE_UNKNOWN;
1981 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1984 TRACE("component %s (installed %d request %d action %d)\n",
1985 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
1988 return ERROR_SUCCESS;
1991 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
1993 MSIPACKAGE *package = param;
1994 LPCWSTR name;
1995 MSIFEATURE *feature;
1997 name = MSI_RecordGetString( row, 1 );
1999 feature = msi_get_loaded_feature( package, name );
2000 if (!feature)
2001 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2002 else
2004 LPCWSTR Condition;
2005 Condition = MSI_RecordGetString(row,3);
2007 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2009 int level = MSI_RecordGetInteger(row,2);
2010 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2011 feature->Level = level;
2014 return ERROR_SUCCESS;
2017 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2019 static const WCHAR name[] = {'\\',0};
2020 VS_FIXEDFILEINFO *ptr, *ret;
2021 LPVOID version;
2022 DWORD versize, handle;
2023 UINT sz;
2025 versize = GetFileVersionInfoSizeW( filename, &handle );
2026 if (!versize)
2027 return NULL;
2029 version = msi_alloc( versize );
2030 if (!version)
2031 return NULL;
2033 GetFileVersionInfoW( filename, 0, versize, version );
2035 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2037 msi_free( version );
2038 return NULL;
2041 ret = msi_alloc( sz );
2042 memcpy( ret, ptr, sz );
2044 msi_free( version );
2045 return ret;
2048 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2050 DWORD ms, ls;
2052 msi_parse_version_string( version, &ms, &ls );
2054 if (fi->dwFileVersionMS > ms) return 1;
2055 else if (fi->dwFileVersionMS < ms) return -1;
2056 else if (fi->dwFileVersionLS > ls) return 1;
2057 else if (fi->dwFileVersionLS < ls) return -1;
2058 return 0;
2061 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2063 DWORD ms1, ms2;
2065 msi_parse_version_string( ver1, &ms1, NULL );
2066 msi_parse_version_string( ver2, &ms2, NULL );
2068 if (ms1 > ms2) return 1;
2069 else if (ms1 < ms2) return -1;
2070 return 0;
2073 DWORD msi_get_disk_file_size( LPCWSTR filename )
2075 HANDLE file;
2076 DWORD size;
2078 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2079 if (file == INVALID_HANDLE_VALUE)
2080 return INVALID_FILE_SIZE;
2082 size = GetFileSize( file, NULL );
2083 TRACE("size is %u\n", size);
2084 CloseHandle( file );
2085 return size;
2088 BOOL msi_file_hash_matches( MSIFILE *file )
2090 UINT r;
2091 MSIFILEHASHINFO hash;
2093 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2094 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2095 if (r != ERROR_SUCCESS)
2096 return FALSE;
2098 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2101 static WCHAR *get_temp_dir( void )
2103 static UINT id;
2104 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2106 GetTempPathW( MAX_PATH, tmp );
2107 for (;;)
2109 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2110 if (CreateDirectoryW( dir, NULL )) break;
2112 return strdupW( dir );
2116 * msi_build_directory_name()
2118 * This function is to save messing round with directory names
2119 * It handles adding backslashes between path segments,
2120 * and can add \ at the end of the directory name if told to.
2122 * It takes a variable number of arguments.
2123 * It always allocates a new string for the result, so make sure
2124 * to free the return value when finished with it.
2126 * The first arg is the number of path segments that follow.
2127 * The arguments following count are a list of path segments.
2128 * A path segment may be NULL.
2130 * Path segments will be added with a \ separating them.
2131 * A \ will not be added after the last segment, however if the
2132 * last segment is NULL, then the last character will be a \
2134 WCHAR *msi_build_directory_name( DWORD count, ... )
2136 DWORD sz = 1, i;
2137 WCHAR *dir;
2138 va_list va;
2140 va_start( va, count );
2141 for (i = 0; i < count; i++)
2143 const WCHAR *str = va_arg( va, const WCHAR * );
2144 if (str) sz += strlenW( str ) + 1;
2146 va_end( va );
2148 dir = msi_alloc( sz * sizeof(WCHAR) );
2149 dir[0] = 0;
2151 va_start( va, count );
2152 for (i = 0; i < count; i++)
2154 const WCHAR *str = va_arg( va, const WCHAR * );
2155 if (!str) continue;
2156 strcatW( dir, str );
2157 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2159 va_end( va );
2160 return dir;
2163 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2165 MSIASSEMBLY *assembly = file->Component->assembly;
2167 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2169 msi_free( file->TargetPath );
2170 if (assembly && !assembly->application)
2172 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2173 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2174 msi_track_tempfile( package, file->TargetPath );
2176 else
2178 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2179 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2182 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2185 static UINT calculate_file_cost( MSIPACKAGE *package )
2187 VS_FIXEDFILEINFO *file_version;
2188 WCHAR *font_version;
2189 MSIFILE *file;
2191 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2193 MSICOMPONENT *comp = file->Component;
2194 DWORD file_size;
2196 if (!comp->Enabled) continue;
2198 if (file->IsCompressed)
2199 comp->ForceLocalState = TRUE;
2201 set_target_path( package, file );
2203 if ((comp->assembly && !comp->assembly->installed) ||
2204 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2206 comp->Cost += file->FileSize;
2207 continue;
2209 file_size = msi_get_disk_file_size( file->TargetPath );
2211 if (file->Version)
2213 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2215 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2217 comp->Cost += file->FileSize - file_size;
2219 msi_free( file_version );
2220 continue;
2222 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2224 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2226 comp->Cost += file->FileSize - file_size;
2228 msi_free( font_version );
2229 continue;
2232 if (file_size != file->FileSize)
2234 comp->Cost += file->FileSize - file_size;
2237 return ERROR_SUCCESS;
2240 WCHAR *msi_normalize_path( const WCHAR *in )
2242 const WCHAR *p = in;
2243 WCHAR *q, *ret;
2244 int n, len = strlenW( in ) + 2;
2246 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2248 len = 0;
2249 while (1)
2251 /* copy until the end of the string or a space */
2252 while (*p != ' ' && (*q = *p))
2254 p++, len++;
2255 /* reduce many backslashes to one */
2256 if (*p != '\\' || *q != '\\')
2257 q++;
2260 /* quit at the end of the string */
2261 if (!*p)
2262 break;
2264 /* count the number of spaces */
2265 n = 0;
2266 while (p[n] == ' ')
2267 n++;
2269 /* if it's leading or trailing space, skip it */
2270 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2271 p += n;
2272 else /* copy n spaces */
2273 while (n && (*q++ = *p++)) n--;
2275 while (q - ret > 0 && q[-1] == ' ') q--;
2276 if (q - ret > 0 && q[-1] != '\\')
2278 q[0] = '\\';
2279 q[1] = 0;
2281 return ret;
2284 static WCHAR *get_install_location( MSIPACKAGE *package )
2286 HKEY hkey;
2287 WCHAR *path;
2289 if (!package->ProductCode) return NULL;
2290 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE ))
2291 return NULL;
2292 path = msi_reg_get_val_str( hkey, szInstallLocation );
2293 RegCloseKey( hkey );
2294 return path;
2297 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2299 FolderList *fl;
2300 MSIFOLDER *folder, *parent, *child;
2301 WCHAR *path, *normalized_path;
2303 TRACE("resolving %s\n", debugstr_w(name));
2305 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2307 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2309 if (!(path = get_install_location( package )) &&
2310 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2312 path = msi_dup_property( package->db, szRootDrive );
2315 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2317 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2319 parent = msi_get_loaded_folder( package, folder->Parent );
2320 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2322 else
2323 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2325 normalized_path = msi_normalize_path( path );
2326 msi_free( path );
2327 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2329 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2330 msi_free( normalized_path );
2331 return;
2333 msi_set_property( package->db, folder->Directory, normalized_path );
2334 msi_free( folder->ResolvedTarget );
2335 folder->ResolvedTarget = normalized_path;
2337 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2339 child = fl->folder;
2340 msi_resolve_target_folder( package, child->Directory, load_prop );
2342 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2345 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2347 static const WCHAR query[] = {
2348 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2349 '`','C','o','n','d','i','t','i','o','n','`',0};
2350 static const WCHAR szOutOfDiskSpace[] = {
2351 'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2352 MSICOMPONENT *comp;
2353 MSIQUERY *view;
2354 LPWSTR level;
2355 UINT rc;
2357 TRACE("Building directory properties\n");
2358 msi_resolve_target_folder( package, szTargetDir, TRUE );
2360 TRACE("Evaluating component conditions\n");
2361 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2363 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2365 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2366 comp->Enabled = FALSE;
2368 else
2369 comp->Enabled = TRUE;
2372 /* read components states from the registry */
2373 ACTION_GetComponentInstallStates(package);
2374 ACTION_GetFeatureInstallStates(package);
2376 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2378 TRACE("Evaluating feature conditions\n");
2380 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2381 if (rc == ERROR_SUCCESS)
2383 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2384 msiobj_release( &view->hdr );
2385 if (rc != ERROR_SUCCESS)
2386 return rc;
2390 TRACE("Calculating file cost\n");
2391 calculate_file_cost( package );
2393 msi_set_property( package->db, szCostingComplete, szOne );
2394 /* set default run level if not set */
2395 level = msi_dup_property( package->db, szInstallLevel );
2396 if (!level)
2397 msi_set_property( package->db, szInstallLevel, szOne );
2398 msi_free(level);
2400 /* FIXME: check volume disk space */
2401 msi_set_property( package->db, szOutOfDiskSpace, szZero );
2403 return MSI_SetFeatureStates(package);
2406 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, DWORD *size)
2408 LPSTR data = NULL;
2410 if (!value)
2412 data = (LPSTR)strdupW(szEmpty);
2413 *size = sizeof(szEmpty);
2414 *type = REG_SZ;
2415 return data;
2417 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2419 if (value[1]=='x')
2421 LPWSTR ptr;
2422 CHAR byte[5];
2423 LPWSTR deformated = NULL;
2424 int count;
2426 deformat_string(package, &value[2], &deformated);
2428 /* binary value type */
2429 ptr = deformated;
2430 *type = REG_BINARY;
2431 if (strlenW(ptr)%2)
2432 *size = (strlenW(ptr)/2)+1;
2433 else
2434 *size = strlenW(ptr)/2;
2436 data = msi_alloc(*size);
2438 byte[0] = '0';
2439 byte[1] = 'x';
2440 byte[4] = 0;
2441 count = 0;
2442 /* if uneven pad with a zero in front */
2443 if (strlenW(ptr)%2)
2445 byte[2]= '0';
2446 byte[3]= *ptr;
2447 ptr++;
2448 data[count] = (BYTE)strtol(byte,NULL,0);
2449 count ++;
2450 TRACE("Uneven byte count\n");
2452 while (*ptr)
2454 byte[2]= *ptr;
2455 ptr++;
2456 byte[3]= *ptr;
2457 ptr++;
2458 data[count] = (BYTE)strtol(byte,NULL,0);
2459 count ++;
2461 msi_free(deformated);
2463 TRACE("Data %i bytes(%i)\n",*size,count);
2465 else
2467 LPWSTR deformated;
2468 LPWSTR p;
2469 DWORD d = 0;
2470 deformat_string(package, &value[1], &deformated);
2472 *type=REG_DWORD;
2473 *size = sizeof(DWORD);
2474 data = msi_alloc(*size);
2475 p = deformated;
2476 if (*p == '-')
2477 p++;
2478 while (*p)
2480 if ( (*p < '0') || (*p > '9') )
2481 break;
2482 d *= 10;
2483 d += (*p - '0');
2484 p++;
2486 if (deformated[0] == '-')
2487 d = -d;
2488 *(LPDWORD)data = d;
2489 TRACE("DWORD %i\n",*(LPDWORD)data);
2491 msi_free(deformated);
2494 else
2496 static const WCHAR szMulti[] = {'[','~',']',0};
2497 LPCWSTR ptr;
2498 *type=REG_SZ;
2500 if (value[0]=='#')
2502 if (value[1]=='%')
2504 ptr = &value[2];
2505 *type=REG_EXPAND_SZ;
2507 else
2508 ptr = &value[1];
2510 else
2511 ptr=value;
2513 if (strstrW(value, szMulti))
2514 *type = REG_MULTI_SZ;
2516 /* remove initial delimiter */
2517 if (!strncmpW(value, szMulti, 3))
2518 ptr = value + 3;
2520 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2522 /* add double NULL terminator */
2523 if (*type == REG_MULTI_SZ)
2525 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2526 data = msi_realloc_zero(data, *size);
2529 return data;
2532 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2534 const WCHAR *ret;
2536 switch (root)
2538 case -1:
2539 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2541 *root_key = HKEY_LOCAL_MACHINE;
2542 ret = szHLM;
2544 else
2546 *root_key = HKEY_CURRENT_USER;
2547 ret = szHCU;
2549 break;
2550 case 0:
2551 *root_key = HKEY_CLASSES_ROOT;
2552 ret = szHCR;
2553 break;
2554 case 1:
2555 *root_key = HKEY_CURRENT_USER;
2556 ret = szHCU;
2557 break;
2558 case 2:
2559 *root_key = HKEY_LOCAL_MACHINE;
2560 ret = szHLM;
2561 break;
2562 case 3:
2563 *root_key = HKEY_USERS;
2564 ret = szHU;
2565 break;
2566 default:
2567 ERR("Unknown root %i\n", root);
2568 return NULL;
2571 return ret;
2574 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2576 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2577 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2579 if ((is_64bit || is_wow64) &&
2580 !(comp->Attributes & msidbComponentAttributes64bit) &&
2581 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2583 UINT size;
2584 WCHAR *path_32node;
2586 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2587 if (!(path_32node = msi_alloc( size ))) return NULL;
2589 memcpy( path_32node, path, len * sizeof(WCHAR) );
2590 strcpyW( path_32node + len, szWow6432Node );
2591 strcatW( path_32node, szBackSlash );
2592 strcatW( path_32node, path + len );
2593 return path_32node;
2595 return strdupW( path );
2598 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2600 REGSAM access = KEY_ALL_ACCESS;
2601 WCHAR *subkey, *p, *q;
2602 HKEY hkey, ret = NULL;
2603 LONG res;
2605 if (is_wow64) access |= KEY_WOW64_64KEY;
2607 if (!(subkey = strdupW( path ))) return NULL;
2608 p = subkey;
2609 if ((q = strchrW( p, '\\' ))) *q = 0;
2610 if (create)
2611 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2612 else
2613 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2614 if (res)
2616 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2617 msi_free( subkey );
2618 return NULL;
2620 if (q && q[1])
2622 ret = open_key( hkey, q + 1, create );
2623 RegCloseKey( hkey );
2625 else ret = hkey;
2626 msi_free( subkey );
2627 return ret;
2630 static BOOL is_special_entry( const WCHAR *name )
2632 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2635 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2637 MSIPACKAGE *package = param;
2638 LPSTR value;
2639 HKEY root_key, hkey;
2640 DWORD type,size;
2641 LPWSTR deformated, uikey, keypath;
2642 LPCWSTR szRoot, component, name, key;
2643 MSICOMPONENT *comp;
2644 MSIRECORD * uirow;
2645 INT root;
2646 BOOL check_first = FALSE;
2647 UINT rc;
2649 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2651 component = MSI_RecordGetString(row, 6);
2652 comp = msi_get_loaded_component(package,component);
2653 if (!comp)
2654 return ERROR_SUCCESS;
2656 comp->Action = msi_get_component_action( package, comp );
2657 if (comp->Action != INSTALLSTATE_LOCAL)
2659 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2660 return ERROR_SUCCESS;
2663 name = MSI_RecordGetString(row, 4);
2664 if( MSI_RecordIsNull(row,5) && name )
2666 /* null values can have special meanings */
2667 if (name[0]=='-' && name[1] == 0)
2668 return ERROR_SUCCESS;
2669 if ((name[0] == '+' || name[0] == '*') && !name[1])
2670 check_first = TRUE;
2673 root = MSI_RecordGetInteger(row,2);
2674 key = MSI_RecordGetString(row, 3);
2676 szRoot = get_root_key( package, root, &root_key );
2677 if (!szRoot)
2678 return ERROR_SUCCESS;
2680 deformat_string(package, key , &deformated);
2681 size = strlenW(deformated) + strlenW(szRoot) + 1;
2682 uikey = msi_alloc(size*sizeof(WCHAR));
2683 strcpyW(uikey,szRoot);
2684 strcatW(uikey,deformated);
2686 keypath = get_keypath( comp, root_key, deformated );
2687 msi_free( deformated );
2688 if (!(hkey = open_key( root_key, keypath, TRUE )))
2690 ERR("Could not create key %s\n", debugstr_w(keypath));
2691 msi_free(uikey);
2692 msi_free(keypath);
2693 return ERROR_FUNCTION_FAILED;
2695 value = parse_value(package, MSI_RecordGetString(row, 5), &type, &size);
2696 deformat_string(package, name, &deformated);
2698 if (!is_special_entry( name ))
2700 if (!check_first)
2702 TRACE("Setting value %s of %s\n", debugstr_w(deformated),
2703 debugstr_w(uikey));
2704 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2706 else
2708 DWORD sz = 0;
2709 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2710 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2712 TRACE("value %s of %s checked already exists\n", debugstr_w(deformated),
2713 debugstr_w(uikey));
2715 else
2717 TRACE("Checked and setting value %s of %s\n", debugstr_w(deformated),
2718 debugstr_w(uikey));
2719 if (deformated || size)
2720 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value, size);
2724 RegCloseKey(hkey);
2726 uirow = MSI_CreateRecord(3);
2727 MSI_RecordSetStringW(uirow,2,deformated);
2728 MSI_RecordSetStringW(uirow,1,uikey);
2729 if (type == REG_SZ || type == REG_EXPAND_SZ)
2730 MSI_RecordSetStringW(uirow, 3, (LPWSTR)value);
2731 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2732 msiobj_release( &uirow->hdr );
2734 msi_free(value);
2735 msi_free(deformated);
2736 msi_free(uikey);
2737 msi_free(keypath);
2739 return ERROR_SUCCESS;
2742 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2744 static const WCHAR query[] = {
2745 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2746 '`','R','e','g','i','s','t','r','y','`',0};
2747 MSIQUERY *view;
2748 UINT rc;
2750 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2751 if (rc != ERROR_SUCCESS)
2752 return ERROR_SUCCESS;
2754 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2755 msiobj_release(&view->hdr);
2756 return rc;
2759 static void delete_key( HKEY root, const WCHAR *path )
2761 REGSAM access = 0;
2762 WCHAR *subkey, *p;
2763 HKEY hkey;
2764 LONG res;
2766 if (is_wow64) access |= KEY_WOW64_64KEY;
2768 if (!(subkey = strdupW( path ))) return;
2769 for (;;)
2771 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2772 hkey = open_key( root, subkey, FALSE );
2773 if (!hkey) break;
2774 if (p && p[1])
2775 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2776 else
2777 res = RegDeleteKeyExW( root, subkey, access, 0 );
2778 if (res)
2780 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2781 break;
2783 if (p && p[1]) RegCloseKey( hkey );
2784 else break;
2786 msi_free( subkey );
2789 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
2791 LONG res;
2792 HKEY hkey;
2793 DWORD num_subkeys, num_values;
2795 if ((hkey = open_key( root, path, FALSE )))
2797 if ((res = RegDeleteValueW( hkey, value )))
2798 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
2800 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
2801 NULL, NULL, NULL, NULL );
2802 RegCloseKey( hkey );
2803 if (!res && !num_subkeys && !num_values)
2805 TRACE("removing empty key %s\n", debugstr_w(path));
2806 delete_key( root, path );
2811 static void delete_tree( HKEY root, const WCHAR *path )
2813 LONG res;
2814 HKEY hkey;
2816 if (!(hkey = open_key( root, path, FALSE ))) return;
2817 res = RegDeleteTreeW( hkey, NULL );
2818 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
2819 delete_key( root, path );
2820 RegCloseKey( hkey );
2823 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
2825 MSIPACKAGE *package = param;
2826 LPCWSTR component, name, key_str, root_key_str;
2827 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2828 MSICOMPONENT *comp;
2829 MSIRECORD *uirow;
2830 BOOL delete_key = FALSE;
2831 HKEY hkey_root;
2832 UINT size;
2833 INT root;
2835 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2837 component = MSI_RecordGetString( row, 6 );
2838 comp = msi_get_loaded_component( package, component );
2839 if (!comp)
2840 return ERROR_SUCCESS;
2842 comp->Action = msi_get_component_action( package, comp );
2843 if (comp->Action != INSTALLSTATE_ABSENT)
2845 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
2846 return ERROR_SUCCESS;
2849 name = MSI_RecordGetString( row, 4 );
2850 if (MSI_RecordIsNull( row, 5 ) && name )
2852 if (name[0] == '+' && !name[1])
2853 return ERROR_SUCCESS;
2854 if ((name[0] == '-' || name[0] == '*') && !name[1])
2856 delete_key = TRUE;
2857 name = NULL;
2861 root = MSI_RecordGetInteger( row, 2 );
2862 key_str = MSI_RecordGetString( row, 3 );
2864 root_key_str = get_root_key( package, root, &hkey_root );
2865 if (!root_key_str)
2866 return ERROR_SUCCESS;
2868 deformat_string( package, key_str, &deformated_key );
2869 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2870 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2871 strcpyW( ui_key_str, root_key_str );
2872 strcatW( ui_key_str, deformated_key );
2874 deformat_string( package, name, &deformated_name );
2876 keypath = get_keypath( comp, hkey_root, deformated_key );
2877 msi_free( deformated_key );
2878 if (delete_key) delete_tree( hkey_root, keypath );
2879 else delete_value( hkey_root, keypath, deformated_name );
2880 msi_free( keypath );
2882 uirow = MSI_CreateRecord( 2 );
2883 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2884 MSI_RecordSetStringW( uirow, 2, deformated_name );
2885 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2886 msiobj_release( &uirow->hdr );
2888 msi_free( ui_key_str );
2889 msi_free( deformated_name );
2890 return ERROR_SUCCESS;
2893 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
2895 MSIPACKAGE *package = param;
2896 LPCWSTR component, name, key_str, root_key_str;
2897 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
2898 MSICOMPONENT *comp;
2899 MSIRECORD *uirow;
2900 BOOL delete_key = FALSE;
2901 HKEY hkey_root;
2902 UINT size;
2903 INT root;
2905 component = MSI_RecordGetString( row, 5 );
2906 comp = msi_get_loaded_component( package, component );
2907 if (!comp)
2908 return ERROR_SUCCESS;
2910 comp->Action = msi_get_component_action( package, comp );
2911 if (comp->Action != INSTALLSTATE_LOCAL)
2913 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2914 return ERROR_SUCCESS;
2917 if ((name = MSI_RecordGetString( row, 4 )))
2919 if (name[0] == '-' && !name[1])
2921 delete_key = TRUE;
2922 name = NULL;
2926 root = MSI_RecordGetInteger( row, 2 );
2927 key_str = MSI_RecordGetString( row, 3 );
2929 root_key_str = get_root_key( package, root, &hkey_root );
2930 if (!root_key_str)
2931 return ERROR_SUCCESS;
2933 deformat_string( package, key_str, &deformated_key );
2934 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
2935 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
2936 strcpyW( ui_key_str, root_key_str );
2937 strcatW( ui_key_str, deformated_key );
2939 deformat_string( package, name, &deformated_name );
2941 keypath = get_keypath( comp, hkey_root, deformated_key );
2942 msi_free( deformated_key );
2943 if (delete_key) delete_tree( hkey_root, keypath );
2944 else delete_value( hkey_root, keypath, deformated_name );
2945 msi_free( keypath );
2947 uirow = MSI_CreateRecord( 2 );
2948 MSI_RecordSetStringW( uirow, 1, ui_key_str );
2949 MSI_RecordSetStringW( uirow, 2, deformated_name );
2950 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
2951 msiobj_release( &uirow->hdr );
2953 msi_free( ui_key_str );
2954 msi_free( deformated_name );
2955 return ERROR_SUCCESS;
2958 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
2960 static const WCHAR registry_query[] = {
2961 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2962 '`','R','e','g','i','s','t','r','y','`',0};
2963 static const WCHAR remove_registry_query[] = {
2964 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2965 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
2966 MSIQUERY *view;
2967 UINT rc;
2969 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
2970 if (rc == ERROR_SUCCESS)
2972 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
2973 msiobj_release( &view->hdr );
2974 if (rc != ERROR_SUCCESS)
2975 return rc;
2977 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
2978 if (rc == ERROR_SUCCESS)
2980 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
2981 msiobj_release( &view->hdr );
2982 if (rc != ERROR_SUCCESS)
2983 return rc;
2985 return ERROR_SUCCESS;
2988 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2990 package->script->CurrentlyScripting = TRUE;
2992 return ERROR_SUCCESS;
2996 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2998 static const WCHAR query[]= {
2999 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3000 '`','R','e','g','i','s','t','r','y','`',0};
3001 MSICOMPONENT *comp;
3002 DWORD total = 0, count = 0;
3003 MSIQUERY *view;
3004 MSIFEATURE *feature;
3005 MSIFILE *file;
3006 UINT rc;
3008 TRACE("InstallValidate\n");
3010 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3011 if (rc == ERROR_SUCCESS)
3013 rc = MSI_IterateRecords( view, &count, NULL, package );
3014 msiobj_release( &view->hdr );
3015 if (rc != ERROR_SUCCESS)
3016 return rc;
3017 total += count * REG_PROGRESS_VALUE;
3019 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3020 total += COMPONENT_PROGRESS_VALUE;
3022 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3023 total += file->FileSize;
3025 msi_ui_progress( package, 0, total, 0, 0 );
3027 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3029 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3030 debugstr_w(feature->Feature), feature->Installed,
3031 feature->ActionRequest, feature->Action);
3033 return ERROR_SUCCESS;
3036 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3038 MSIPACKAGE* package = param;
3039 LPCWSTR cond = NULL;
3040 LPCWSTR message = NULL;
3041 UINT r;
3043 static const WCHAR title[]=
3044 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3046 cond = MSI_RecordGetString(row,1);
3048 r = MSI_EvaluateConditionW(package,cond);
3049 if (r == MSICONDITION_FALSE)
3051 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3053 LPWSTR deformated;
3054 message = MSI_RecordGetString(row,2);
3055 deformat_string(package,message,&deformated);
3056 MessageBoxW(NULL,deformated,title,MB_OK);
3057 msi_free(deformated);
3060 return ERROR_INSTALL_FAILURE;
3063 return ERROR_SUCCESS;
3066 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3068 static const WCHAR query[] = {
3069 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3070 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3071 MSIQUERY *view;
3072 UINT rc;
3074 TRACE("Checking launch conditions\n");
3076 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3077 if (rc != ERROR_SUCCESS)
3078 return ERROR_SUCCESS;
3080 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3081 msiobj_release(&view->hdr);
3082 return rc;
3085 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3088 if (!cmp->KeyPath)
3089 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3091 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3093 static const WCHAR query[] = {
3094 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3095 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3096 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3097 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3098 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3099 MSIRECORD *row;
3100 UINT root, len;
3101 LPWSTR deformated, buffer, deformated_name;
3102 LPCWSTR key, name;
3104 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3105 if (!row)
3106 return NULL;
3108 root = MSI_RecordGetInteger(row,2);
3109 key = MSI_RecordGetString(row, 3);
3110 name = MSI_RecordGetString(row, 4);
3111 deformat_string(package, key , &deformated);
3112 deformat_string(package, name, &deformated_name);
3114 len = strlenW(deformated) + 6;
3115 if (deformated_name)
3116 len+=strlenW(deformated_name);
3118 buffer = msi_alloc( len *sizeof(WCHAR));
3120 if (deformated_name)
3121 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3122 else
3123 sprintfW(buffer,fmt,root,deformated);
3125 msi_free(deformated);
3126 msi_free(deformated_name);
3127 msiobj_release(&row->hdr);
3129 return buffer;
3131 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3133 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3134 return NULL;
3136 else
3138 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3140 if (file)
3141 return strdupW( file->TargetPath );
3143 return NULL;
3146 static HKEY openSharedDLLsKey(void)
3148 HKEY hkey=0;
3149 static const WCHAR path[] =
3150 {'S','o','f','t','w','a','r','e','\\',
3151 'M','i','c','r','o','s','o','f','t','\\',
3152 'W','i','n','d','o','w','s','\\',
3153 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3154 'S','h','a','r','e','d','D','L','L','s',0};
3156 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3157 return hkey;
3160 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3162 HKEY hkey;
3163 DWORD count=0;
3164 DWORD type;
3165 DWORD sz = sizeof(count);
3166 DWORD rc;
3168 hkey = openSharedDLLsKey();
3169 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3170 if (rc != ERROR_SUCCESS)
3171 count = 0;
3172 RegCloseKey(hkey);
3173 return count;
3176 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3178 HKEY hkey;
3180 hkey = openSharedDLLsKey();
3181 if (count > 0)
3182 msi_reg_set_val_dword( hkey, path, count );
3183 else
3184 RegDeleteValueW(hkey,path);
3185 RegCloseKey(hkey);
3186 return count;
3189 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3191 MSIFEATURE *feature;
3192 INT count = 0;
3193 BOOL write = FALSE;
3195 /* only refcount DLLs */
3196 if (comp->KeyPath == NULL ||
3197 comp->assembly ||
3198 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3199 comp->Attributes & msidbComponentAttributesODBCDataSource)
3200 write = FALSE;
3201 else
3203 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3204 write = (count > 0);
3206 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3207 write = TRUE;
3210 /* increment counts */
3211 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3213 ComponentList *cl;
3215 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3216 continue;
3218 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3220 if ( cl->component == comp )
3221 count++;
3225 /* decrement counts */
3226 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3228 ComponentList *cl;
3230 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3231 continue;
3233 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3235 if ( cl->component == comp )
3236 count--;
3240 /* ref count all the files in the component */
3241 if (write)
3243 MSIFILE *file;
3245 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3247 if (file->Component == comp)
3248 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3252 /* add a count for permanent */
3253 if (comp->Attributes & msidbComponentAttributesPermanent)
3254 count ++;
3256 comp->RefCount = count;
3258 if (write)
3259 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3262 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3264 if (comp->assembly)
3266 const WCHAR prefixW[] = {'<','\\',0};
3267 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3268 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3270 if (keypath)
3272 strcpyW( keypath, prefixW );
3273 strcatW( keypath, comp->assembly->display_name );
3275 return keypath;
3277 return resolve_keypath( package, comp );
3280 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3282 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3283 UINT rc;
3284 MSICOMPONENT *comp;
3285 HKEY hkey;
3287 TRACE("\n");
3289 squash_guid(package->ProductCode,squished_pc);
3290 msi_set_sourcedir_props(package, FALSE);
3292 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3294 MSIRECORD *uirow;
3295 INSTALLSTATE action;
3297 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3298 if (!comp->ComponentId)
3299 continue;
3301 squash_guid( comp->ComponentId, squished_cc );
3302 msi_free( comp->FullKeypath );
3303 comp->FullKeypath = build_full_keypath( package, comp );
3305 ACTION_RefCountComponent( package, comp );
3307 if (package->need_rollback) action = comp->Installed;
3308 else action = comp->ActionRequest;
3310 TRACE("Component %s (%s), Keypath=%s, RefCount=%u Action=%u\n",
3311 debugstr_w(comp->Component), debugstr_w(squished_cc),
3312 debugstr_w(comp->FullKeypath), comp->RefCount, action);
3314 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3316 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3317 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3318 else
3319 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3321 if (rc != ERROR_SUCCESS)
3322 continue;
3324 if (comp->Attributes & msidbComponentAttributesPermanent)
3326 static const WCHAR szPermKey[] =
3327 { '0','0','0','0','0','0','0','0','0','0','0','0',
3328 '0','0','0','0','0','0','0','0','0','0','0','0',
3329 '0','0','0','0','0','0','0','0',0 };
3331 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3333 if (action == INSTALLSTATE_LOCAL)
3334 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3335 else
3337 MSIFILE *file;
3338 MSIRECORD *row;
3339 LPWSTR ptr, ptr2;
3340 WCHAR source[MAX_PATH];
3341 WCHAR base[MAX_PATH];
3342 LPWSTR sourcepath;
3344 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3345 static const WCHAR query[] = {
3346 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3347 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3348 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3349 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3350 '`','D','i','s','k','I','d','`',0};
3352 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3353 continue;
3355 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3356 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3357 ptr2 = strrchrW(source, '\\') + 1;
3358 msiobj_release(&row->hdr);
3360 lstrcpyW(base, package->PackagePath);
3361 ptr = strrchrW(base, '\\');
3362 *(ptr + 1) = '\0';
3364 sourcepath = msi_resolve_file_source(package, file);
3365 ptr = sourcepath + lstrlenW(base);
3366 lstrcpyW(ptr2, ptr);
3367 msi_free(sourcepath);
3369 msi_reg_set_val_str(hkey, squished_pc, source);
3371 RegCloseKey(hkey);
3373 else if (action == INSTALLSTATE_ABSENT)
3375 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3376 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3377 else
3378 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3381 /* UI stuff */
3382 uirow = MSI_CreateRecord(3);
3383 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3384 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3385 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3386 msi_ui_actiondata( package, szProcessComponents, uirow );
3387 msiobj_release( &uirow->hdr );
3389 return ERROR_SUCCESS;
3392 typedef struct {
3393 CLSID clsid;
3394 LPWSTR source;
3396 LPWSTR path;
3397 ITypeLib *ptLib;
3398 } typelib_struct;
3400 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3401 LPWSTR lpszName, LONG_PTR lParam)
3403 TLIBATTR *attr;
3404 typelib_struct *tl_struct = (typelib_struct*) lParam;
3405 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3406 int sz;
3407 HRESULT res;
3409 if (!IS_INTRESOURCE(lpszName))
3411 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3412 return TRUE;
3415 sz = strlenW(tl_struct->source)+4;
3416 sz *= sizeof(WCHAR);
3418 if ((INT_PTR)lpszName == 1)
3419 tl_struct->path = strdupW(tl_struct->source);
3420 else
3422 tl_struct->path = msi_alloc(sz);
3423 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3426 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3427 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3428 if (FAILED(res))
3430 msi_free(tl_struct->path);
3431 tl_struct->path = NULL;
3433 return TRUE;
3436 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3437 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3439 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3440 return FALSE;
3443 msi_free(tl_struct->path);
3444 tl_struct->path = NULL;
3446 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3447 ITypeLib_Release(tl_struct->ptLib);
3449 return TRUE;
3452 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3454 MSIPACKAGE* package = param;
3455 LPCWSTR component;
3456 MSICOMPONENT *comp;
3457 MSIFILE *file;
3458 typelib_struct tl_struct;
3459 ITypeLib *tlib;
3460 HMODULE module;
3461 HRESULT hr;
3463 component = MSI_RecordGetString(row,3);
3464 comp = msi_get_loaded_component(package,component);
3465 if (!comp)
3466 return ERROR_SUCCESS;
3468 comp->Action = msi_get_component_action( package, comp );
3469 if (comp->Action != INSTALLSTATE_LOCAL)
3471 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3472 return ERROR_SUCCESS;
3475 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3477 TRACE("component has no key path\n");
3478 return ERROR_SUCCESS;
3480 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3482 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3483 if (module)
3485 LPCWSTR guid;
3486 guid = MSI_RecordGetString(row,1);
3487 CLSIDFromString( guid, &tl_struct.clsid);
3488 tl_struct.source = strdupW( file->TargetPath );
3489 tl_struct.path = NULL;
3491 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3492 (LONG_PTR)&tl_struct);
3494 if (tl_struct.path)
3496 LPCWSTR helpid, help_path = NULL;
3497 HRESULT res;
3499 helpid = MSI_RecordGetString(row,6);
3501 if (helpid) help_path = msi_get_target_folder( package, helpid );
3502 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3504 if (FAILED(res))
3505 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3506 else
3507 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3509 ITypeLib_Release(tl_struct.ptLib);
3510 msi_free(tl_struct.path);
3512 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3514 FreeLibrary(module);
3515 msi_free(tl_struct.source);
3517 else
3519 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3520 if (FAILED(hr))
3522 ERR("Failed to load type library: %08x\n", hr);
3523 return ERROR_INSTALL_FAILURE;
3526 ITypeLib_Release(tlib);
3529 return ERROR_SUCCESS;
3532 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3534 static const WCHAR query[] = {
3535 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3536 '`','T','y','p','e','L','i','b','`',0};
3537 MSIQUERY *view;
3538 UINT rc;
3540 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3541 if (rc != ERROR_SUCCESS)
3542 return ERROR_SUCCESS;
3544 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3545 msiobj_release(&view->hdr);
3546 return rc;
3549 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3551 MSIPACKAGE *package = param;
3552 LPCWSTR component, guid;
3553 MSICOMPONENT *comp;
3554 GUID libid;
3555 UINT version;
3556 LCID language;
3557 SYSKIND syskind;
3558 HRESULT hr;
3560 component = MSI_RecordGetString( row, 3 );
3561 comp = msi_get_loaded_component( package, component );
3562 if (!comp)
3563 return ERROR_SUCCESS;
3565 comp->Action = msi_get_component_action( package, comp );
3566 if (comp->Action != INSTALLSTATE_ABSENT)
3568 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3569 return ERROR_SUCCESS;
3571 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3573 guid = MSI_RecordGetString( row, 1 );
3574 CLSIDFromString( guid, &libid );
3575 version = MSI_RecordGetInteger( row, 4 );
3576 language = MSI_RecordGetInteger( row, 2 );
3578 #ifdef _WIN64
3579 syskind = SYS_WIN64;
3580 #else
3581 syskind = SYS_WIN32;
3582 #endif
3584 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3585 if (FAILED(hr))
3587 WARN("Failed to unregister typelib: %08x\n", hr);
3590 return ERROR_SUCCESS;
3593 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3595 static const WCHAR query[] = {
3596 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3597 '`','T','y','p','e','L','i','b','`',0};
3598 MSIQUERY *view;
3599 UINT rc;
3601 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3602 if (rc != ERROR_SUCCESS)
3603 return ERROR_SUCCESS;
3605 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3606 msiobj_release( &view->hdr );
3607 return rc;
3610 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3612 static const WCHAR szlnk[] = {'.','l','n','k',0};
3613 LPCWSTR directory, extension, link_folder;
3614 LPWSTR link_file, filename;
3616 directory = MSI_RecordGetString( row, 2 );
3617 link_folder = msi_get_target_folder( package, directory );
3618 if (!link_folder)
3620 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3621 return NULL;
3623 /* may be needed because of a bug somewhere else */
3624 msi_create_full_path( link_folder );
3626 filename = msi_dup_record_field( row, 3 );
3627 msi_reduce_to_long_filename( filename );
3629 extension = strchrW( filename, '.' );
3630 if (!extension || strcmpiW( extension, szlnk ))
3632 int len = strlenW( filename );
3633 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3634 memcpy( filename + len, szlnk, sizeof(szlnk) );
3636 link_file = msi_build_directory_name( 2, link_folder, filename );
3637 msi_free( filename );
3639 return link_file;
3642 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3644 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3645 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3646 WCHAR *folder, *dest, *path;
3648 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3649 folder = msi_dup_property( package->db, szWindowsFolder );
3650 else
3652 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3653 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3654 msi_free( appdata );
3656 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3657 msi_create_full_path( dest );
3658 path = msi_build_directory_name( 2, dest, icon_name );
3659 msi_free( folder );
3660 msi_free( dest );
3661 return path;
3664 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3666 MSIPACKAGE *package = param;
3667 LPWSTR link_file, deformated, path;
3668 LPCWSTR component, target;
3669 MSICOMPONENT *comp;
3670 IShellLinkW *sl = NULL;
3671 IPersistFile *pf = NULL;
3672 HRESULT res;
3674 component = MSI_RecordGetString(row, 4);
3675 comp = msi_get_loaded_component(package, component);
3676 if (!comp)
3677 return ERROR_SUCCESS;
3679 comp->Action = msi_get_component_action( package, comp );
3680 if (comp->Action != INSTALLSTATE_LOCAL)
3682 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3683 return ERROR_SUCCESS;
3685 msi_ui_actiondata( package, szCreateShortcuts, row );
3687 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3688 &IID_IShellLinkW, (LPVOID *) &sl );
3690 if (FAILED( res ))
3692 ERR("CLSID_ShellLink not available\n");
3693 goto err;
3696 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3697 if (FAILED( res ))
3699 ERR("QueryInterface(IID_IPersistFile) failed\n");
3700 goto err;
3703 target = MSI_RecordGetString(row, 5);
3704 if (strchrW(target, '['))
3706 deformat_string( package, target, &path );
3707 TRACE("target path is %s\n", debugstr_w(path));
3708 IShellLinkW_SetPath( sl, path );
3709 msi_free( path );
3711 else
3713 FIXME("poorly handled shortcut format, advertised shortcut\n");
3714 IShellLinkW_SetPath(sl,comp->FullKeypath);
3717 if (!MSI_RecordIsNull(row,6))
3719 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3720 deformat_string(package, arguments, &deformated);
3721 IShellLinkW_SetArguments(sl,deformated);
3722 msi_free(deformated);
3725 if (!MSI_RecordIsNull(row,7))
3727 LPCWSTR description = MSI_RecordGetString(row, 7);
3728 IShellLinkW_SetDescription(sl, description);
3731 if (!MSI_RecordIsNull(row,8))
3732 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3734 if (!MSI_RecordIsNull(row,9))
3736 INT index;
3737 LPCWSTR icon = MSI_RecordGetString(row, 9);
3739 path = msi_build_icon_path(package, icon);
3740 index = MSI_RecordGetInteger(row,10);
3742 /* no value means 0 */
3743 if (index == MSI_NULL_INTEGER)
3744 index = 0;
3746 IShellLinkW_SetIconLocation(sl, path, index);
3747 msi_free(path);
3750 if (!MSI_RecordIsNull(row,11))
3751 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3753 if (!MSI_RecordIsNull(row,12))
3755 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3756 full_path = msi_get_target_folder( package, wkdir );
3757 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3759 link_file = get_link_file(package, row);
3761 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3762 IPersistFile_Save(pf, link_file, FALSE);
3763 msi_free(link_file);
3765 err:
3766 if (pf)
3767 IPersistFile_Release( pf );
3768 if (sl)
3769 IShellLinkW_Release( sl );
3771 return ERROR_SUCCESS;
3774 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3776 static const WCHAR query[] = {
3777 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3778 '`','S','h','o','r','t','c','u','t','`',0};
3779 MSIQUERY *view;
3780 HRESULT res;
3781 UINT rc;
3783 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3784 if (rc != ERROR_SUCCESS)
3785 return ERROR_SUCCESS;
3787 res = CoInitialize( NULL );
3789 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3790 msiobj_release(&view->hdr);
3792 if (SUCCEEDED(res)) CoUninitialize();
3793 return rc;
3796 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
3798 MSIPACKAGE *package = param;
3799 LPWSTR link_file;
3800 LPCWSTR component;
3801 MSICOMPONENT *comp;
3803 component = MSI_RecordGetString( row, 4 );
3804 comp = msi_get_loaded_component( package, component );
3805 if (!comp)
3806 return ERROR_SUCCESS;
3808 comp->Action = msi_get_component_action( package, comp );
3809 if (comp->Action != INSTALLSTATE_ABSENT)
3811 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3812 return ERROR_SUCCESS;
3814 msi_ui_actiondata( package, szRemoveShortcuts, row );
3816 link_file = get_link_file( package, row );
3818 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
3819 if (!DeleteFileW( link_file ))
3821 WARN("Failed to remove shortcut file %u\n", GetLastError());
3823 msi_free( link_file );
3825 return ERROR_SUCCESS;
3828 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
3830 static const WCHAR query[] = {
3831 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3832 '`','S','h','o','r','t','c','u','t','`',0};
3833 MSIQUERY *view;
3834 UINT rc;
3836 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3837 if (rc != ERROR_SUCCESS)
3838 return ERROR_SUCCESS;
3840 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
3841 msiobj_release( &view->hdr );
3842 return rc;
3845 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3847 MSIPACKAGE* package = param;
3848 HANDLE the_file;
3849 LPWSTR FilePath;
3850 LPCWSTR FileName;
3851 CHAR buffer[1024];
3852 DWORD sz;
3853 UINT rc;
3855 FileName = MSI_RecordGetString(row,1);
3856 if (!FileName)
3858 ERR("Unable to get FileName\n");
3859 return ERROR_SUCCESS;
3862 FilePath = msi_build_icon_path(package, FileName);
3864 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3866 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3867 FILE_ATTRIBUTE_NORMAL, NULL);
3869 if (the_file == INVALID_HANDLE_VALUE)
3871 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3872 msi_free(FilePath);
3873 return ERROR_SUCCESS;
3878 DWORD write;
3879 sz = 1024;
3880 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3881 if (rc != ERROR_SUCCESS)
3883 ERR("Failed to get stream\n");
3884 CloseHandle(the_file);
3885 DeleteFileW(FilePath);
3886 break;
3888 WriteFile(the_file,buffer,sz,&write,NULL);
3889 } while (sz == 1024);
3891 msi_free(FilePath);
3892 CloseHandle(the_file);
3894 return ERROR_SUCCESS;
3897 static UINT msi_publish_icons(MSIPACKAGE *package)
3899 static const WCHAR query[]= {
3900 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3901 '`','I','c','o','n','`',0};
3902 MSIQUERY *view;
3903 UINT r;
3905 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3906 if (r == ERROR_SUCCESS)
3908 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3909 msiobj_release(&view->hdr);
3910 if (r != ERROR_SUCCESS)
3911 return r;
3913 return ERROR_SUCCESS;
3916 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3918 UINT r;
3919 HKEY source;
3920 LPWSTR buffer;
3921 MSIMEDIADISK *disk;
3922 MSISOURCELISTINFO *info;
3924 r = RegCreateKeyW(hkey, szSourceList, &source);
3925 if (r != ERROR_SUCCESS)
3926 return r;
3928 RegCloseKey(source);
3930 buffer = strrchrW(package->PackagePath, '\\') + 1;
3931 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3932 package->Context, MSICODE_PRODUCT,
3933 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3934 if (r != ERROR_SUCCESS)
3935 return r;
3937 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3938 package->Context, MSICODE_PRODUCT,
3939 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3940 if (r != ERROR_SUCCESS)
3941 return r;
3943 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3944 package->Context, MSICODE_PRODUCT,
3945 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3946 if (r != ERROR_SUCCESS)
3947 return r;
3949 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3951 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
3952 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3953 info->options, info->value);
3954 else
3955 MsiSourceListSetInfoW(package->ProductCode, NULL,
3956 info->context, info->options,
3957 info->property, info->value);
3960 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3962 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3963 disk->context, disk->options,
3964 disk->disk_id, disk->volume_label, disk->disk_prompt);
3967 return ERROR_SUCCESS;
3970 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3972 MSIHANDLE hdb, suminfo;
3973 WCHAR guids[MAX_PATH];
3974 WCHAR packcode[SQUISH_GUID_SIZE];
3975 LPWSTR buffer;
3976 LPWSTR ptr;
3977 DWORD langid;
3978 DWORD size;
3979 UINT r;
3981 static const WCHAR szARPProductIcon[] =
3982 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3983 static const WCHAR szAssignment[] =
3984 {'A','s','s','i','g','n','m','e','n','t',0};
3985 static const WCHAR szAdvertiseFlags[] =
3986 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3987 static const WCHAR szClients[] =
3988 {'C','l','i','e','n','t','s',0};
3989 static const WCHAR szColon[] = {':',0};
3991 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
3992 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3993 msi_free(buffer);
3995 langid = msi_get_property_int(package->db, szProductLanguage, 0);
3996 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3998 /* FIXME */
3999 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4001 buffer = msi_dup_property(package->db, szARPProductIcon);
4002 if (buffer)
4004 LPWSTR path = msi_build_icon_path(package, buffer);
4005 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4006 msi_free(path);
4007 msi_free(buffer);
4010 buffer = msi_dup_property(package->db, szProductVersion);
4011 if (buffer)
4013 DWORD verdword = msi_version_str_to_dword(buffer);
4014 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4015 msi_free(buffer);
4018 msi_reg_set_val_dword(hkey, szAssignment, 0);
4019 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4020 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4021 msi_reg_set_val_str(hkey, szClients, szColon);
4023 hdb = alloc_msihandle(&package->db->hdr);
4024 if (!hdb)
4025 return ERROR_NOT_ENOUGH_MEMORY;
4027 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4028 MsiCloseHandle(hdb);
4029 if (r != ERROR_SUCCESS)
4030 goto done;
4032 size = MAX_PATH;
4033 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4034 NULL, guids, &size);
4035 if (r != ERROR_SUCCESS)
4036 goto done;
4038 ptr = strchrW(guids, ';');
4039 if (ptr) *ptr = 0;
4040 squash_guid(guids, packcode);
4041 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4043 done:
4044 MsiCloseHandle(suminfo);
4045 return ERROR_SUCCESS;
4048 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4050 UINT r;
4051 HKEY hkey;
4052 LPWSTR upgrade;
4053 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4055 upgrade = msi_dup_property(package->db, szUpgradeCode);
4056 if (!upgrade)
4057 return ERROR_SUCCESS;
4059 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4060 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4061 else
4062 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4064 if (r != ERROR_SUCCESS)
4066 WARN("failed to open upgrade code key\n");
4067 msi_free(upgrade);
4068 return ERROR_SUCCESS;
4070 squash_guid(package->ProductCode, squashed_pc);
4071 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4072 RegCloseKey(hkey);
4073 msi_free(upgrade);
4074 return ERROR_SUCCESS;
4077 static BOOL msi_check_publish(MSIPACKAGE *package)
4079 MSIFEATURE *feature;
4081 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4083 feature->Action = msi_get_feature_action( package, feature );
4084 if (feature->Action == INSTALLSTATE_LOCAL)
4085 return TRUE;
4088 return FALSE;
4091 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4093 MSIFEATURE *feature;
4095 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4097 feature->Action = msi_get_feature_action( package, feature );
4098 if (feature->Action != INSTALLSTATE_ABSENT)
4099 return FALSE;
4102 return TRUE;
4105 static UINT msi_publish_patches( MSIPACKAGE *package )
4107 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4108 WCHAR patch_squashed[GUID_SIZE];
4109 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4110 LONG res;
4111 MSIPATCHINFO *patch;
4112 UINT r;
4113 WCHAR *p, *all_patches = NULL;
4114 DWORD len = 0;
4116 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4117 if (r != ERROR_SUCCESS)
4118 return ERROR_FUNCTION_FAILED;
4120 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4121 if (res != ERROR_SUCCESS)
4123 r = ERROR_FUNCTION_FAILED;
4124 goto done;
4127 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4128 if (r != ERROR_SUCCESS)
4129 goto done;
4131 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4133 squash_guid( patch->patchcode, patch_squashed );
4134 len += strlenW( patch_squashed ) + 1;
4137 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4138 if (!all_patches)
4139 goto done;
4141 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4143 HKEY patch_key;
4145 squash_guid( patch->patchcode, p );
4146 p += strlenW( p ) + 1;
4148 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4149 (const BYTE *)patch->transforms,
4150 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4151 if (res != ERROR_SUCCESS)
4152 goto done;
4154 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4155 if (r != ERROR_SUCCESS)
4156 goto done;
4158 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4159 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4160 RegCloseKey( patch_key );
4161 if (res != ERROR_SUCCESS)
4162 goto done;
4164 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4166 res = GetLastError();
4167 ERR("Unable to copy patch package %d\n", res);
4168 goto done;
4170 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4171 if (res != ERROR_SUCCESS)
4172 goto done;
4174 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4175 RegCloseKey( patch_key );
4176 if (res != ERROR_SUCCESS)
4177 goto done;
4180 all_patches[len] = 0;
4181 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4182 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4183 if (res != ERROR_SUCCESS)
4184 goto done;
4186 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4187 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4188 if (res != ERROR_SUCCESS)
4189 r = ERROR_FUNCTION_FAILED;
4191 done:
4192 RegCloseKey( product_patches_key );
4193 RegCloseKey( patches_key );
4194 RegCloseKey( product_key );
4195 msi_free( all_patches );
4196 return r;
4199 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4201 UINT rc;
4202 HKEY hukey = NULL, hudkey = NULL;
4203 MSIRECORD *uirow;
4205 if (!list_empty(&package->patches))
4207 rc = msi_publish_patches(package);
4208 if (rc != ERROR_SUCCESS)
4209 goto end;
4212 /* FIXME: also need to publish if the product is in advertise mode */
4213 if (!msi_check_publish(package))
4214 return ERROR_SUCCESS;
4216 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4217 &hukey, TRUE);
4218 if (rc != ERROR_SUCCESS)
4219 goto end;
4221 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4222 NULL, &hudkey, TRUE);
4223 if (rc != ERROR_SUCCESS)
4224 goto end;
4226 rc = msi_publish_upgrade_code(package);
4227 if (rc != ERROR_SUCCESS)
4228 goto end;
4230 rc = msi_publish_product_properties(package, hukey);
4231 if (rc != ERROR_SUCCESS)
4232 goto end;
4234 rc = msi_publish_sourcelist(package, hukey);
4235 if (rc != ERROR_SUCCESS)
4236 goto end;
4238 rc = msi_publish_icons(package);
4240 end:
4241 uirow = MSI_CreateRecord( 1 );
4242 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4243 msi_ui_actiondata( package, szPublishProduct, uirow );
4244 msiobj_release( &uirow->hdr );
4246 RegCloseKey(hukey);
4247 RegCloseKey(hudkey);
4248 return rc;
4251 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4253 WCHAR *filename, *ptr, *folder, *ret;
4254 const WCHAR *dirprop;
4256 filename = msi_dup_record_field( row, 2 );
4257 if (filename && (ptr = strchrW( filename, '|' )))
4258 ptr++;
4259 else
4260 ptr = filename;
4262 dirprop = MSI_RecordGetString( row, 3 );
4263 if (dirprop)
4265 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4266 if (!folder) folder = msi_dup_property( package->db, dirprop );
4268 else
4269 folder = msi_dup_property( package->db, szWindowsFolder );
4271 if (!folder)
4273 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4274 msi_free( filename );
4275 return NULL;
4278 ret = msi_build_directory_name( 2, folder, ptr );
4280 msi_free( filename );
4281 msi_free( folder );
4282 return ret;
4285 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4287 MSIPACKAGE *package = param;
4288 LPCWSTR component, section, key, value, identifier;
4289 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4290 MSIRECORD * uirow;
4291 INT action;
4292 MSICOMPONENT *comp;
4294 component = MSI_RecordGetString(row, 8);
4295 comp = msi_get_loaded_component(package,component);
4296 if (!comp)
4297 return ERROR_SUCCESS;
4299 comp->Action = msi_get_component_action( package, comp );
4300 if (comp->Action != INSTALLSTATE_LOCAL)
4302 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4303 return ERROR_SUCCESS;
4306 identifier = MSI_RecordGetString(row,1);
4307 section = MSI_RecordGetString(row,4);
4308 key = MSI_RecordGetString(row,5);
4309 value = MSI_RecordGetString(row,6);
4310 action = MSI_RecordGetInteger(row,7);
4312 deformat_string(package,section,&deformated_section);
4313 deformat_string(package,key,&deformated_key);
4314 deformat_string(package,value,&deformated_value);
4316 fullname = get_ini_file_name(package, row);
4318 if (action == 0)
4320 TRACE("Adding value %s to section %s in %s\n",
4321 debugstr_w(deformated_key), debugstr_w(deformated_section),
4322 debugstr_w(fullname));
4323 WritePrivateProfileStringW(deformated_section, deformated_key,
4324 deformated_value, fullname);
4326 else if (action == 1)
4328 WCHAR returned[10];
4329 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4330 returned, 10, fullname);
4331 if (returned[0] == 0)
4333 TRACE("Adding value %s to section %s in %s\n",
4334 debugstr_w(deformated_key), debugstr_w(deformated_section),
4335 debugstr_w(fullname));
4337 WritePrivateProfileStringW(deformated_section, deformated_key,
4338 deformated_value, fullname);
4341 else if (action == 3)
4342 FIXME("Append to existing section not yet implemented\n");
4344 uirow = MSI_CreateRecord(4);
4345 MSI_RecordSetStringW(uirow,1,identifier);
4346 MSI_RecordSetStringW(uirow,2,deformated_section);
4347 MSI_RecordSetStringW(uirow,3,deformated_key);
4348 MSI_RecordSetStringW(uirow,4,deformated_value);
4349 msi_ui_actiondata( package, szWriteIniValues, uirow );
4350 msiobj_release( &uirow->hdr );
4352 msi_free(fullname);
4353 msi_free(deformated_key);
4354 msi_free(deformated_value);
4355 msi_free(deformated_section);
4356 return ERROR_SUCCESS;
4359 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4361 static const WCHAR query[] = {
4362 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4363 '`','I','n','i','F','i','l','e','`',0};
4364 MSIQUERY *view;
4365 UINT rc;
4367 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4368 if (rc != ERROR_SUCCESS)
4369 return ERROR_SUCCESS;
4371 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4372 msiobj_release(&view->hdr);
4373 return rc;
4376 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4378 MSIPACKAGE *package = param;
4379 LPCWSTR component, section, key, value, identifier;
4380 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4381 MSICOMPONENT *comp;
4382 MSIRECORD *uirow;
4383 INT action;
4385 component = MSI_RecordGetString( row, 8 );
4386 comp = msi_get_loaded_component( package, component );
4387 if (!comp)
4388 return ERROR_SUCCESS;
4390 comp->Action = msi_get_component_action( package, comp );
4391 if (comp->Action != INSTALLSTATE_ABSENT)
4393 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4394 return ERROR_SUCCESS;
4397 identifier = MSI_RecordGetString( row, 1 );
4398 section = MSI_RecordGetString( row, 4 );
4399 key = MSI_RecordGetString( row, 5 );
4400 value = MSI_RecordGetString( row, 6 );
4401 action = MSI_RecordGetInteger( row, 7 );
4403 deformat_string( package, section, &deformated_section );
4404 deformat_string( package, key, &deformated_key );
4405 deformat_string( package, value, &deformated_value );
4407 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4409 filename = get_ini_file_name( package, row );
4411 TRACE("Removing key %s from section %s in %s\n",
4412 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4414 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4416 WARN("Unable to remove key %u\n", GetLastError());
4418 msi_free( filename );
4420 else
4421 FIXME("Unsupported action %d\n", action);
4424 uirow = MSI_CreateRecord( 4 );
4425 MSI_RecordSetStringW( uirow, 1, identifier );
4426 MSI_RecordSetStringW( uirow, 2, deformated_section );
4427 MSI_RecordSetStringW( uirow, 3, deformated_key );
4428 MSI_RecordSetStringW( uirow, 4, deformated_value );
4429 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4430 msiobj_release( &uirow->hdr );
4432 msi_free( deformated_key );
4433 msi_free( deformated_value );
4434 msi_free( deformated_section );
4435 return ERROR_SUCCESS;
4438 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4440 MSIPACKAGE *package = param;
4441 LPCWSTR component, section, key, value, identifier;
4442 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4443 MSICOMPONENT *comp;
4444 MSIRECORD *uirow;
4445 INT action;
4447 component = MSI_RecordGetString( row, 8 );
4448 comp = msi_get_loaded_component( package, component );
4449 if (!comp)
4450 return ERROR_SUCCESS;
4452 comp->Action = msi_get_component_action( package, comp );
4453 if (comp->Action != INSTALLSTATE_LOCAL)
4455 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4456 return ERROR_SUCCESS;
4459 identifier = MSI_RecordGetString( row, 1 );
4460 section = MSI_RecordGetString( row, 4 );
4461 key = MSI_RecordGetString( row, 5 );
4462 value = MSI_RecordGetString( row, 6 );
4463 action = MSI_RecordGetInteger( row, 7 );
4465 deformat_string( package, section, &deformated_section );
4466 deformat_string( package, key, &deformated_key );
4467 deformat_string( package, value, &deformated_value );
4469 if (action == msidbIniFileActionRemoveLine)
4471 filename = get_ini_file_name( package, row );
4473 TRACE("Removing key %s from section %s in %s\n",
4474 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4476 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4478 WARN("Unable to remove key %u\n", GetLastError());
4480 msi_free( filename );
4482 else
4483 FIXME("Unsupported action %d\n", action);
4485 uirow = MSI_CreateRecord( 4 );
4486 MSI_RecordSetStringW( uirow, 1, identifier );
4487 MSI_RecordSetStringW( uirow, 2, deformated_section );
4488 MSI_RecordSetStringW( uirow, 3, deformated_key );
4489 MSI_RecordSetStringW( uirow, 4, deformated_value );
4490 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4491 msiobj_release( &uirow->hdr );
4493 msi_free( deformated_key );
4494 msi_free( deformated_value );
4495 msi_free( deformated_section );
4496 return ERROR_SUCCESS;
4499 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4501 static const WCHAR query[] = {
4502 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4503 '`','I','n','i','F','i','l','e','`',0};
4504 static const WCHAR remove_query[] = {
4505 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4506 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4507 MSIQUERY *view;
4508 UINT rc;
4510 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4511 if (rc == ERROR_SUCCESS)
4513 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4514 msiobj_release( &view->hdr );
4515 if (rc != ERROR_SUCCESS)
4516 return rc;
4518 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4519 if (rc == ERROR_SUCCESS)
4521 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4522 msiobj_release( &view->hdr );
4523 if (rc != ERROR_SUCCESS)
4524 return rc;
4526 return ERROR_SUCCESS;
4529 static void register_dll( const WCHAR *dll, BOOL unregister )
4531 HMODULE hmod;
4533 hmod = LoadLibraryExW( dll, 0, LOAD_WITH_ALTERED_SEARCH_PATH );
4534 if (hmod)
4536 HRESULT (WINAPI *func_ptr)( void );
4537 const char *func = unregister ? "DllUnregisterServer" : "DllRegisterServer";
4539 func_ptr = (void *)GetProcAddress( hmod, func );
4540 if (func_ptr)
4542 HRESULT hr = func_ptr();
4543 if (FAILED( hr ))
4544 WARN("failed to register dll 0x%08x\n", hr);
4546 else
4547 WARN("entry point %s not found\n", func);
4548 FreeLibrary( hmod );
4549 return;
4551 WARN("failed to load library %u\n", GetLastError());
4554 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4556 MSIPACKAGE *package = param;
4557 LPCWSTR filename;
4558 MSIFILE *file;
4559 MSIRECORD *uirow;
4561 filename = MSI_RecordGetString( row, 1 );
4562 file = msi_get_loaded_file( package, filename );
4563 if (!file)
4565 WARN("unable to find file %s\n", debugstr_w(filename));
4566 return ERROR_SUCCESS;
4568 file->Component->Action = msi_get_component_action( package, file->Component );
4569 if (file->Component->Action != INSTALLSTATE_LOCAL)
4571 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4572 return ERROR_SUCCESS;
4575 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4576 register_dll( file->TargetPath, FALSE );
4578 uirow = MSI_CreateRecord( 2 );
4579 MSI_RecordSetStringW( uirow, 1, file->File );
4580 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4581 msi_ui_actiondata( package, szSelfRegModules, uirow );
4582 msiobj_release( &uirow->hdr );
4584 return ERROR_SUCCESS;
4587 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4589 static const WCHAR query[] = {
4590 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4591 '`','S','e','l','f','R','e','g','`',0};
4592 MSIQUERY *view;
4593 UINT rc;
4595 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4596 if (rc != ERROR_SUCCESS)
4597 return ERROR_SUCCESS;
4599 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4600 msiobj_release(&view->hdr);
4601 return rc;
4604 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4606 MSIPACKAGE *package = param;
4607 LPCWSTR filename;
4608 MSIFILE *file;
4609 MSIRECORD *uirow;
4611 filename = MSI_RecordGetString( row, 1 );
4612 file = msi_get_loaded_file( package, filename );
4613 if (!file)
4615 WARN("unable to find file %s\n", debugstr_w(filename));
4616 return ERROR_SUCCESS;
4618 file->Component->Action = msi_get_component_action( package, file->Component );
4619 if (file->Component->Action != INSTALLSTATE_ABSENT)
4621 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4622 return ERROR_SUCCESS;
4625 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4626 register_dll( file->TargetPath, TRUE );
4628 uirow = MSI_CreateRecord( 2 );
4629 MSI_RecordSetStringW( uirow, 1, file->File );
4630 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4631 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4632 msiobj_release( &uirow->hdr );
4634 return ERROR_SUCCESS;
4637 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4639 static const WCHAR query[] = {
4640 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4641 '`','S','e','l','f','R','e','g','`',0};
4642 MSIQUERY *view;
4643 UINT rc;
4645 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4646 if (rc != ERROR_SUCCESS)
4647 return ERROR_SUCCESS;
4649 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4650 msiobj_release( &view->hdr );
4651 return rc;
4654 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4656 MSIFEATURE *feature;
4657 UINT rc;
4658 HKEY hkey = NULL, userdata = NULL;
4660 if (!msi_check_publish(package))
4661 return ERROR_SUCCESS;
4663 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4664 &hkey, TRUE);
4665 if (rc != ERROR_SUCCESS)
4666 goto end;
4668 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4669 &userdata, TRUE);
4670 if (rc != ERROR_SUCCESS)
4671 goto end;
4673 /* here the guids are base 85 encoded */
4674 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4676 ComponentList *cl;
4677 LPWSTR data = NULL;
4678 GUID clsid;
4679 INT size;
4680 BOOL absent = FALSE;
4681 MSIRECORD *uirow;
4683 if (feature->Action != INSTALLSTATE_LOCAL &&
4684 feature->Action != INSTALLSTATE_SOURCE &&
4685 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4687 size = 1;
4688 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4690 size += 21;
4692 if (feature->Feature_Parent)
4693 size += strlenW( feature->Feature_Parent )+2;
4695 data = msi_alloc(size * sizeof(WCHAR));
4697 data[0] = 0;
4698 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4700 MSICOMPONENT* component = cl->component;
4701 WCHAR buf[21];
4703 buf[0] = 0;
4704 if (component->ComponentId)
4706 TRACE("From %s\n",debugstr_w(component->ComponentId));
4707 CLSIDFromString(component->ComponentId, &clsid);
4708 encode_base85_guid(&clsid,buf);
4709 TRACE("to %s\n",debugstr_w(buf));
4710 strcatW(data,buf);
4714 if (feature->Feature_Parent)
4716 static const WCHAR sep[] = {'\2',0};
4717 strcatW(data,sep);
4718 strcatW(data,feature->Feature_Parent);
4721 msi_reg_set_val_str( userdata, feature->Feature, data );
4722 msi_free(data);
4724 size = 0;
4725 if (feature->Feature_Parent)
4726 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4727 if (!absent)
4729 size += sizeof(WCHAR);
4730 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4731 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4733 else
4735 size += 2*sizeof(WCHAR);
4736 data = msi_alloc(size);
4737 data[0] = 0x6;
4738 data[1] = 0;
4739 if (feature->Feature_Parent)
4740 strcpyW( &data[1], feature->Feature_Parent );
4741 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4742 (LPBYTE)data,size);
4743 msi_free(data);
4746 /* the UI chunk */
4747 uirow = MSI_CreateRecord( 1 );
4748 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4749 msi_ui_actiondata( package, szPublishFeatures, uirow );
4750 msiobj_release( &uirow->hdr );
4751 /* FIXME: call msi_ui_progress? */
4754 end:
4755 RegCloseKey(hkey);
4756 RegCloseKey(userdata);
4757 return rc;
4760 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4762 UINT r;
4763 HKEY hkey;
4764 MSIRECORD *uirow;
4766 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4768 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4769 &hkey, FALSE);
4770 if (r == ERROR_SUCCESS)
4772 RegDeleteValueW(hkey, feature->Feature);
4773 RegCloseKey(hkey);
4776 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4777 &hkey, FALSE);
4778 if (r == ERROR_SUCCESS)
4780 RegDeleteValueW(hkey, feature->Feature);
4781 RegCloseKey(hkey);
4784 uirow = MSI_CreateRecord( 1 );
4785 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4786 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
4787 msiobj_release( &uirow->hdr );
4789 return ERROR_SUCCESS;
4792 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4794 MSIFEATURE *feature;
4796 if (!msi_check_unpublish(package))
4797 return ERROR_SUCCESS;
4799 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4801 msi_unpublish_feature(package, feature);
4804 return ERROR_SUCCESS;
4807 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4809 SYSTEMTIME systime;
4810 DWORD size, langid;
4811 WCHAR date[9], *val, *buffer;
4812 const WCHAR *prop, *key;
4814 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4815 static const WCHAR modpath_fmt[] =
4816 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4817 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4818 static const WCHAR szModifyPath[] =
4819 {'M','o','d','i','f','y','P','a','t','h',0};
4820 static const WCHAR szUninstallString[] =
4821 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4822 static const WCHAR szEstimatedSize[] =
4823 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4824 static const WCHAR szDisplayVersion[] =
4825 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4826 static const WCHAR szInstallSource[] =
4827 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
4828 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
4829 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
4830 static const WCHAR szAuthorizedCDFPrefix[] =
4831 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
4832 static const WCHAR szARPCONTACT[] =
4833 {'A','R','P','C','O','N','T','A','C','T',0};
4834 static const WCHAR szContact[] =
4835 {'C','o','n','t','a','c','t',0};
4836 static const WCHAR szARPCOMMENTS[] =
4837 {'A','R','P','C','O','M','M','E','N','T','S',0};
4838 static const WCHAR szComments[] =
4839 {'C','o','m','m','e','n','t','s',0};
4840 static const WCHAR szProductName[] =
4841 {'P','r','o','d','u','c','t','N','a','m','e',0};
4842 static const WCHAR szDisplayName[] =
4843 {'D','i','s','p','l','a','y','N','a','m','e',0};
4844 static const WCHAR szARPHELPLINK[] =
4845 {'A','R','P','H','E','L','P','L','I','N','K',0};
4846 static const WCHAR szHelpLink[] =
4847 {'H','e','l','p','L','i','n','k',0};
4848 static const WCHAR szARPHELPTELEPHONE[] =
4849 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
4850 static const WCHAR szHelpTelephone[] =
4851 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
4852 static const WCHAR szARPINSTALLLOCATION[] =
4853 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
4854 static const WCHAR szManufacturer[] =
4855 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4856 static const WCHAR szPublisher[] =
4857 {'P','u','b','l','i','s','h','e','r',0};
4858 static const WCHAR szARPREADME[] =
4859 {'A','R','P','R','E','A','D','M','E',0};
4860 static const WCHAR szReadme[] =
4861 {'R','e','a','d','M','e',0};
4862 static const WCHAR szARPSIZE[] =
4863 {'A','R','P','S','I','Z','E',0};
4864 static const WCHAR szSize[] =
4865 {'S','i','z','e',0};
4866 static const WCHAR szARPURLINFOABOUT[] =
4867 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
4868 static const WCHAR szURLInfoAbout[] =
4869 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
4870 static const WCHAR szARPURLUPDATEINFO[] =
4871 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
4872 static const WCHAR szURLUpdateInfo[] =
4873 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
4874 static const WCHAR szARPSYSTEMCOMPONENT[] =
4875 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
4876 static const WCHAR szSystemComponent[] =
4877 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
4879 static const WCHAR *propval[] = {
4880 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
4881 szARPCONTACT, szContact,
4882 szARPCOMMENTS, szComments,
4883 szProductName, szDisplayName,
4884 szARPHELPLINK, szHelpLink,
4885 szARPHELPTELEPHONE, szHelpTelephone,
4886 szARPINSTALLLOCATION, szInstallLocation,
4887 szSourceDir, szInstallSource,
4888 szManufacturer, szPublisher,
4889 szARPREADME, szReadme,
4890 szARPSIZE, szSize,
4891 szARPURLINFOABOUT, szURLInfoAbout,
4892 szARPURLUPDATEINFO, szURLUpdateInfo,
4893 NULL
4895 const WCHAR **p = propval;
4897 while (*p)
4899 prop = *p++;
4900 key = *p++;
4901 val = msi_dup_property(package->db, prop);
4902 msi_reg_set_val_str(hkey, key, val);
4903 msi_free(val);
4906 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4907 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
4909 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
4911 size = deformat_string(package, modpath_fmt, &buffer);
4912 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4913 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4914 msi_free(buffer);
4916 /* FIXME: Write real Estimated Size when we have it */
4917 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4919 GetLocalTime(&systime);
4920 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4921 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4923 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4924 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4926 buffer = msi_dup_property(package->db, szProductVersion);
4927 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4928 if (buffer)
4930 DWORD verdword = msi_version_str_to_dword(buffer);
4932 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4933 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4934 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4935 msi_free(buffer);
4938 return ERROR_SUCCESS;
4941 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4943 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4944 MSIRECORD *uirow;
4945 LPWSTR upgrade_code;
4946 HKEY hkey, props, upgrade_key;
4947 UINT rc;
4949 /* FIXME: also need to publish if the product is in advertise mode */
4950 if (!msi_check_publish(package))
4951 return ERROR_SUCCESS;
4953 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
4954 if (rc != ERROR_SUCCESS)
4955 return rc;
4957 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
4958 if (rc != ERROR_SUCCESS)
4959 goto done;
4961 rc = msi_publish_install_properties(package, hkey);
4962 if (rc != ERROR_SUCCESS)
4963 goto done;
4965 rc = msi_publish_install_properties(package, props);
4966 if (rc != ERROR_SUCCESS)
4967 goto done;
4969 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
4970 if (upgrade_code)
4972 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
4973 if (rc == ERROR_SUCCESS)
4975 squash_guid( package->ProductCode, squashed_pc );
4976 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
4977 RegCloseKey( upgrade_key );
4979 msi_free( upgrade_code );
4981 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
4982 package->delete_on_close = FALSE;
4984 done:
4985 uirow = MSI_CreateRecord( 1 );
4986 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4987 msi_ui_actiondata( package, szRegisterProduct, uirow );
4988 msiobj_release( &uirow->hdr );
4990 RegCloseKey(hkey);
4991 return ERROR_SUCCESS;
4994 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4996 return execute_script(package, SCRIPT_INSTALL);
4999 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5001 MSIPACKAGE *package = param;
5002 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5003 WCHAR *p, *icon_path;
5005 if (!icon) return ERROR_SUCCESS;
5006 if ((icon_path = msi_build_icon_path( package, icon )))
5008 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5009 DeleteFileW( icon_path );
5010 if ((p = strrchrW( icon_path, '\\' )))
5012 *p = 0;
5013 RemoveDirectoryW( icon_path );
5015 msi_free( icon_path );
5017 return ERROR_SUCCESS;
5020 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5022 static const WCHAR query[]= {
5023 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5024 MSIQUERY *view;
5025 UINT r;
5027 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5028 if (r == ERROR_SUCCESS)
5030 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5031 msiobj_release( &view->hdr );
5032 if (r != ERROR_SUCCESS)
5033 return r;
5035 return ERROR_SUCCESS;
5038 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5040 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5041 WCHAR *upgrade, **features;
5042 BOOL full_uninstall = TRUE;
5043 MSIFEATURE *feature;
5044 MSIPATCHINFO *patch;
5045 UINT i;
5047 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5049 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5051 features = msi_split_string( remove, ',' );
5052 for (i = 0; features && features[i]; i++)
5054 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5056 msi_free(features);
5058 if (!full_uninstall)
5059 return ERROR_SUCCESS;
5061 MSIREG_DeleteProductKey(package->ProductCode);
5062 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5063 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5065 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5066 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5067 MSIREG_DeleteUserProductKey(package->ProductCode);
5068 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5070 upgrade = msi_dup_property(package->db, szUpgradeCode);
5071 if (upgrade)
5073 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5074 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5075 msi_free(upgrade);
5078 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5080 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5081 if (!strcmpW( package->ProductCode, patch->products ))
5083 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5084 patch->delete_on_close = TRUE;
5086 /* FIXME: remove local patch package if this is the last product */
5088 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5089 package->delete_on_close = TRUE;
5091 msi_unpublish_icons( package );
5092 return ERROR_SUCCESS;
5095 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5097 UINT rc;
5098 WCHAR *remove;
5100 /* turn off scheduling */
5101 package->script->CurrentlyScripting= FALSE;
5103 /* first do the same as an InstallExecute */
5104 rc = ACTION_InstallExecute(package);
5105 if (rc != ERROR_SUCCESS)
5106 return rc;
5108 /* then handle commit actions */
5109 rc = execute_script(package, SCRIPT_COMMIT);
5110 if (rc != ERROR_SUCCESS)
5111 return rc;
5113 remove = msi_dup_property(package->db, szRemove);
5114 rc = msi_unpublish_product(package, remove);
5115 msi_free(remove);
5116 return rc;
5119 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5121 static const WCHAR RunOnce[] = {
5122 'S','o','f','t','w','a','r','e','\\',
5123 'M','i','c','r','o','s','o','f','t','\\',
5124 'W','i','n','d','o','w','s','\\',
5125 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5126 'R','u','n','O','n','c','e',0};
5127 static const WCHAR InstallRunOnce[] = {
5128 'S','o','f','t','w','a','r','e','\\',
5129 'M','i','c','r','o','s','o','f','t','\\',
5130 'W','i','n','d','o','w','s','\\',
5131 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5132 'I','n','s','t','a','l','l','e','r','\\',
5133 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5135 static const WCHAR msiexec_fmt[] = {
5136 '%','s',
5137 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5138 '\"','%','s','\"',0};
5139 static const WCHAR install_fmt[] = {
5140 '/','I',' ','\"','%','s','\"',' ',
5141 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5142 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5143 WCHAR buffer[256], sysdir[MAX_PATH];
5144 HKEY hkey;
5145 WCHAR squished_pc[100];
5147 squash_guid(package->ProductCode,squished_pc);
5149 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5150 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5151 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5152 squished_pc);
5154 msi_reg_set_val_str( hkey, squished_pc, buffer );
5155 RegCloseKey(hkey);
5157 TRACE("Reboot command %s\n",debugstr_w(buffer));
5159 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5160 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5162 msi_reg_set_val_str( hkey, squished_pc, buffer );
5163 RegCloseKey(hkey);
5165 return ERROR_INSTALL_SUSPEND;
5168 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5170 static const WCHAR query[] =
5171 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5172 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5173 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5174 MSIRECORD *rec, *row;
5175 DWORD i, size = 0;
5176 va_list va;
5177 const WCHAR *str;
5178 WCHAR *data;
5180 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5182 rec = MSI_CreateRecord( count + 2 );
5183 str = MSI_RecordGetString( row, 1 );
5184 MSI_RecordSetStringW( rec, 0, str );
5185 msiobj_release( &row->hdr );
5186 MSI_RecordSetInteger( rec, 1, error );
5188 va_start( va, count );
5189 for (i = 0; i < count; i++)
5191 str = va_arg( va, const WCHAR *);
5192 MSI_RecordSetStringW( rec, i + 2, str );
5194 va_end( va );
5196 MSI_FormatRecordW( package, rec, NULL, &size );
5197 size++;
5198 data = msi_alloc( size * sizeof(WCHAR) );
5199 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5200 else data[0] = 0;
5201 msiobj_release( &rec->hdr );
5202 return data;
5205 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5207 DWORD attrib;
5208 UINT rc;
5211 * We are currently doing what should be done here in the top level Install
5212 * however for Administrative and uninstalls this step will be needed
5214 if (!package->PackagePath)
5215 return ERROR_SUCCESS;
5217 msi_set_sourcedir_props(package, TRUE);
5219 attrib = GetFileAttributesW(package->db->path);
5220 if (attrib == INVALID_FILE_ATTRIBUTES)
5222 LPWSTR prompt, msg;
5223 DWORD size = 0;
5225 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5226 package->Context, MSICODE_PRODUCT,
5227 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5228 if (rc == ERROR_MORE_DATA)
5230 prompt = msi_alloc(size * sizeof(WCHAR));
5231 MsiSourceListGetInfoW(package->ProductCode, NULL,
5232 package->Context, MSICODE_PRODUCT,
5233 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5235 else
5236 prompt = strdupW(package->db->path);
5238 msg = msi_build_error_string(package, 1302, 1, prompt);
5239 msi_free(prompt);
5240 while(attrib == INVALID_FILE_ATTRIBUTES)
5242 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5243 if (rc == IDCANCEL)
5245 msi_free(msg);
5246 return ERROR_INSTALL_USEREXIT;
5248 attrib = GetFileAttributesW(package->db->path);
5250 msi_free(msg);
5251 rc = ERROR_SUCCESS;
5253 else
5254 return ERROR_SUCCESS;
5256 return rc;
5259 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5261 HKEY hkey = 0;
5262 LPWSTR buffer, productid = NULL;
5263 UINT i, rc = ERROR_SUCCESS;
5264 MSIRECORD *uirow;
5266 static const WCHAR szPropKeys[][80] =
5268 {'P','r','o','d','u','c','t','I','D',0},
5269 {'U','S','E','R','N','A','M','E',0},
5270 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5271 {0},
5274 static const WCHAR szRegKeys[][80] =
5276 {'P','r','o','d','u','c','t','I','D',0},
5277 {'R','e','g','O','w','n','e','r',0},
5278 {'R','e','g','C','o','m','p','a','n','y',0},
5279 {0},
5282 if (msi_check_unpublish(package))
5284 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5285 goto end;
5288 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5289 if (!productid)
5290 goto end;
5292 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5293 NULL, &hkey, TRUE);
5294 if (rc != ERROR_SUCCESS)
5295 goto end;
5297 for( i = 0; szPropKeys[i][0]; i++ )
5299 buffer = msi_dup_property( package->db, szPropKeys[i] );
5300 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5301 msi_free( buffer );
5304 end:
5305 uirow = MSI_CreateRecord( 1 );
5306 MSI_RecordSetStringW( uirow, 1, productid );
5307 msi_ui_actiondata( package, szRegisterUser, uirow );
5308 msiobj_release( &uirow->hdr );
5310 msi_free(productid);
5311 RegCloseKey(hkey);
5312 return rc;
5316 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5318 UINT rc;
5320 package->script->InWhatSequence |= SEQUENCE_EXEC;
5321 rc = ACTION_ProcessExecSequence(package,FALSE);
5322 return rc;
5325 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5327 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5328 WCHAR productid_85[21], component_85[21], *ret;
5329 GUID clsid;
5330 DWORD sz;
5332 /* > is used if there is a component GUID and < if not. */
5334 productid_85[0] = 0;
5335 component_85[0] = 0;
5336 CLSIDFromString( package->ProductCode, &clsid );
5338 encode_base85_guid( &clsid, productid_85 );
5339 if (component)
5341 CLSIDFromString( component->ComponentId, &clsid );
5342 encode_base85_guid( &clsid, component_85 );
5345 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5346 debugstr_w(component_85));
5348 sz = 20 + strlenW( feature ) + 20 + 3;
5349 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5350 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5351 return ret;
5354 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5356 MSIPACKAGE *package = param;
5357 LPCWSTR compgroupid, component, feature, qualifier, text;
5358 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5359 HKEY hkey = NULL;
5360 UINT rc;
5361 MSICOMPONENT *comp;
5362 MSIFEATURE *feat;
5363 DWORD sz;
5364 MSIRECORD *uirow;
5365 int len;
5367 feature = MSI_RecordGetString(rec, 5);
5368 feat = msi_get_loaded_feature(package, feature);
5369 if (!feat)
5370 return ERROR_SUCCESS;
5372 feat->Action = msi_get_feature_action( package, feat );
5373 if (feat->Action != INSTALLSTATE_LOCAL &&
5374 feat->Action != INSTALLSTATE_SOURCE &&
5375 feat->Action != INSTALLSTATE_ADVERTISED)
5377 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5378 return ERROR_SUCCESS;
5381 component = MSI_RecordGetString(rec, 3);
5382 comp = msi_get_loaded_component(package, component);
5383 if (!comp)
5384 return ERROR_SUCCESS;
5386 compgroupid = MSI_RecordGetString(rec,1);
5387 qualifier = MSI_RecordGetString(rec,2);
5389 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5390 if (rc != ERROR_SUCCESS)
5391 goto end;
5393 advertise = msi_create_component_advertise_string( package, comp, feature );
5394 text = MSI_RecordGetString( rec, 4 );
5395 if (text)
5397 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5398 strcpyW( p, advertise );
5399 strcatW( p, text );
5400 msi_free( advertise );
5401 advertise = p;
5403 existing = msi_reg_get_val_str( hkey, qualifier );
5405 sz = strlenW( advertise ) + 1;
5406 if (existing)
5408 for (p = existing; *p; p += len)
5410 len = strlenW( p ) + 1;
5411 if (strcmpW( advertise, p )) sz += len;
5414 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5416 rc = ERROR_OUTOFMEMORY;
5417 goto end;
5419 q = output;
5420 if (existing)
5422 for (p = existing; *p; p += len)
5424 len = strlenW( p ) + 1;
5425 if (strcmpW( advertise, p ))
5427 memcpy( q, p, len * sizeof(WCHAR) );
5428 q += len;
5432 strcpyW( q, advertise );
5433 q[strlenW( q ) + 1] = 0;
5435 msi_reg_set_val_multi_str( hkey, qualifier, output );
5437 end:
5438 RegCloseKey(hkey);
5439 msi_free( output );
5440 msi_free( advertise );
5441 msi_free( existing );
5443 /* the UI chunk */
5444 uirow = MSI_CreateRecord( 2 );
5445 MSI_RecordSetStringW( uirow, 1, compgroupid );
5446 MSI_RecordSetStringW( uirow, 2, qualifier);
5447 msi_ui_actiondata( package, szPublishComponents, uirow );
5448 msiobj_release( &uirow->hdr );
5449 /* FIXME: call ui_progress? */
5451 return rc;
5455 * At present I am ignorning the advertised components part of this and only
5456 * focusing on the qualified component sets
5458 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5460 static const WCHAR query[] = {
5461 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5462 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5463 MSIQUERY *view;
5464 UINT rc;
5466 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5467 if (rc != ERROR_SUCCESS)
5468 return ERROR_SUCCESS;
5470 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5471 msiobj_release(&view->hdr);
5472 return rc;
5475 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5477 static const WCHAR szInstallerComponents[] = {
5478 'S','o','f','t','w','a','r','e','\\',
5479 'M','i','c','r','o','s','o','f','t','\\',
5480 'I','n','s','t','a','l','l','e','r','\\',
5481 'C','o','m','p','o','n','e','n','t','s','\\',0};
5483 MSIPACKAGE *package = param;
5484 LPCWSTR compgroupid, component, feature, qualifier;
5485 MSICOMPONENT *comp;
5486 MSIFEATURE *feat;
5487 MSIRECORD *uirow;
5488 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5489 LONG res;
5491 feature = MSI_RecordGetString( rec, 5 );
5492 feat = msi_get_loaded_feature( package, feature );
5493 if (!feat)
5494 return ERROR_SUCCESS;
5496 feat->Action = msi_get_feature_action( package, feat );
5497 if (feat->Action != INSTALLSTATE_ABSENT)
5499 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5500 return ERROR_SUCCESS;
5503 component = MSI_RecordGetString( rec, 3 );
5504 comp = msi_get_loaded_component( package, component );
5505 if (!comp)
5506 return ERROR_SUCCESS;
5508 compgroupid = MSI_RecordGetString( rec, 1 );
5509 qualifier = MSI_RecordGetString( rec, 2 );
5511 squash_guid( compgroupid, squashed );
5512 strcpyW( keypath, szInstallerComponents );
5513 strcatW( keypath, squashed );
5515 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5516 if (res != ERROR_SUCCESS)
5518 WARN("Unable to delete component key %d\n", res);
5521 uirow = MSI_CreateRecord( 2 );
5522 MSI_RecordSetStringW( uirow, 1, compgroupid );
5523 MSI_RecordSetStringW( uirow, 2, qualifier );
5524 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5525 msiobj_release( &uirow->hdr );
5527 return ERROR_SUCCESS;
5530 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5532 static const WCHAR query[] = {
5533 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5534 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5535 MSIQUERY *view;
5536 UINT rc;
5538 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5539 if (rc != ERROR_SUCCESS)
5540 return ERROR_SUCCESS;
5542 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5543 msiobj_release( &view->hdr );
5544 return rc;
5547 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5549 static const WCHAR query[] =
5550 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5551 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5552 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5553 MSIPACKAGE *package = param;
5554 MSICOMPONENT *component;
5555 MSIRECORD *row;
5556 MSIFILE *file;
5557 SC_HANDLE hscm = NULL, service = NULL;
5558 LPCWSTR comp, key;
5559 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5560 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5561 DWORD serv_type, start_type, err_control;
5562 SERVICE_DESCRIPTIONW sd = {NULL};
5564 comp = MSI_RecordGetString( rec, 12 );
5565 component = msi_get_loaded_component( package, comp );
5566 if (!component)
5568 WARN("service component not found\n");
5569 goto done;
5571 component->Action = msi_get_component_action( package, component );
5572 if (component->Action != INSTALLSTATE_LOCAL)
5574 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5575 goto done;
5577 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5578 if (!hscm)
5580 ERR("Failed to open the SC Manager!\n");
5581 goto done;
5584 start_type = MSI_RecordGetInteger(rec, 5);
5585 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5586 goto done;
5588 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5589 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5590 serv_type = MSI_RecordGetInteger(rec, 4);
5591 err_control = MSI_RecordGetInteger(rec, 6);
5592 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5593 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5594 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5595 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5596 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5597 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5599 /* fetch the service path */
5600 row = MSI_QueryGetRecord(package->db, query, comp);
5601 if (!row)
5603 ERR("Query failed\n");
5604 goto done;
5606 key = MSI_RecordGetString(row, 6);
5607 file = msi_get_loaded_file(package, key);
5608 msiobj_release(&row->hdr);
5609 if (!file)
5611 ERR("Failed to load the service file\n");
5612 goto done;
5615 if (!args || !args[0]) image_path = file->TargetPath;
5616 else
5618 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5619 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5620 return ERROR_OUTOFMEMORY;
5622 strcpyW(image_path, file->TargetPath);
5623 strcatW(image_path, szSpace);
5624 strcatW(image_path, args);
5626 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5627 start_type, err_control, image_path, load_order,
5628 NULL, depends, serv_name, pass);
5630 if (!service)
5632 if (GetLastError() != ERROR_SERVICE_EXISTS)
5633 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5635 else if (sd.lpDescription)
5637 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5638 WARN("failed to set service description %u\n", GetLastError());
5641 if (image_path != file->TargetPath) msi_free(image_path);
5642 done:
5643 CloseServiceHandle(service);
5644 CloseServiceHandle(hscm);
5645 msi_free(name);
5646 msi_free(disp);
5647 msi_free(sd.lpDescription);
5648 msi_free(load_order);
5649 msi_free(serv_name);
5650 msi_free(pass);
5651 msi_free(depends);
5652 msi_free(args);
5654 return ERROR_SUCCESS;
5657 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5659 static const WCHAR query[] = {
5660 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5661 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5662 MSIQUERY *view;
5663 UINT rc;
5665 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5666 if (rc != ERROR_SUCCESS)
5667 return ERROR_SUCCESS;
5669 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5670 msiobj_release(&view->hdr);
5671 return rc;
5674 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5675 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5677 LPCWSTR *vector, *temp_vector;
5678 LPWSTR p, q;
5679 DWORD sep_len;
5681 static const WCHAR separator[] = {'[','~',']',0};
5683 *numargs = 0;
5684 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5686 if (!args)
5687 return NULL;
5689 vector = msi_alloc(sizeof(LPWSTR));
5690 if (!vector)
5691 return NULL;
5693 p = args;
5696 (*numargs)++;
5697 vector[*numargs - 1] = p;
5699 if ((q = strstrW(p, separator)))
5701 *q = '\0';
5703 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5704 if (!temp_vector)
5706 msi_free(vector);
5707 return NULL;
5709 vector = temp_vector;
5711 p = q + sep_len;
5713 } while (q);
5715 return vector;
5718 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5720 MSIPACKAGE *package = param;
5721 MSICOMPONENT *comp;
5722 MSIRECORD *uirow;
5723 SC_HANDLE scm = NULL, service = NULL;
5724 LPCWSTR component, *vector = NULL;
5725 LPWSTR name, args, display_name = NULL;
5726 DWORD event, numargs, len, wait, dummy;
5727 UINT r = ERROR_FUNCTION_FAILED;
5728 SERVICE_STATUS_PROCESS status;
5729 ULONGLONG start_time;
5731 component = MSI_RecordGetString(rec, 6);
5732 comp = msi_get_loaded_component(package, component);
5733 if (!comp)
5734 return ERROR_SUCCESS;
5736 comp->Action = msi_get_component_action( package, comp );
5737 if (comp->Action != INSTALLSTATE_LOCAL)
5739 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5740 return ERROR_SUCCESS;
5743 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5744 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5745 event = MSI_RecordGetInteger(rec, 3);
5746 wait = MSI_RecordGetInteger(rec, 5);
5748 if (!(event & msidbServiceControlEventStart))
5750 r = ERROR_SUCCESS;
5751 goto done;
5754 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5755 if (!scm)
5757 ERR("Failed to open the service control manager\n");
5758 goto done;
5761 len = 0;
5762 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5763 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5765 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5766 GetServiceDisplayNameW( scm, name, display_name, &len );
5769 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5770 if (!service)
5772 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5773 goto done;
5776 vector = msi_service_args_to_vector(args, &numargs);
5778 if (!StartServiceW(service, numargs, vector) &&
5779 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
5781 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
5782 goto done;
5785 r = ERROR_SUCCESS;
5786 if (wait)
5788 /* wait for at most 30 seconds for the service to be up and running */
5789 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5790 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5792 TRACE("failed to query service status (%u)\n", GetLastError());
5793 goto done;
5795 start_time = GetTickCount64();
5796 while (status.dwCurrentState == SERVICE_START_PENDING)
5798 if (GetTickCount64() - start_time > 30000) break;
5799 Sleep(1000);
5800 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
5801 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
5803 TRACE("failed to query service status (%u)\n", GetLastError());
5804 goto done;
5807 if (status.dwCurrentState != SERVICE_RUNNING)
5809 WARN("service failed to start %u\n", status.dwCurrentState);
5810 r = ERROR_FUNCTION_FAILED;
5814 done:
5815 uirow = MSI_CreateRecord( 2 );
5816 MSI_RecordSetStringW( uirow, 1, display_name );
5817 MSI_RecordSetStringW( uirow, 2, name );
5818 msi_ui_actiondata( package, szStartServices, uirow );
5819 msiobj_release( &uirow->hdr );
5821 CloseServiceHandle(service);
5822 CloseServiceHandle(scm);
5824 msi_free(name);
5825 msi_free(args);
5826 msi_free(vector);
5827 msi_free(display_name);
5828 return r;
5831 static UINT ACTION_StartServices( MSIPACKAGE *package )
5833 static const WCHAR query[] = {
5834 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5835 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5836 MSIQUERY *view;
5837 UINT rc;
5839 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5840 if (rc != ERROR_SUCCESS)
5841 return ERROR_SUCCESS;
5843 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
5844 msiobj_release(&view->hdr);
5845 return rc;
5848 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
5850 DWORD i, needed, count;
5851 ENUM_SERVICE_STATUSW *dependencies;
5852 SERVICE_STATUS ss;
5853 SC_HANDLE depserv;
5855 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
5856 0, &needed, &count))
5857 return TRUE;
5859 if (GetLastError() != ERROR_MORE_DATA)
5860 return FALSE;
5862 dependencies = msi_alloc(needed);
5863 if (!dependencies)
5864 return FALSE;
5866 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
5867 needed, &needed, &count))
5868 goto error;
5870 for (i = 0; i < count; i++)
5872 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
5873 SERVICE_STOP | SERVICE_QUERY_STATUS);
5874 if (!depserv)
5875 goto error;
5877 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
5878 goto error;
5881 return TRUE;
5883 error:
5884 msi_free(dependencies);
5885 return FALSE;
5888 static UINT stop_service( LPCWSTR name )
5890 SC_HANDLE scm = NULL, service = NULL;
5891 SERVICE_STATUS status;
5892 SERVICE_STATUS_PROCESS ssp;
5893 DWORD needed;
5895 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
5896 if (!scm)
5898 WARN("Failed to open the SCM: %d\n", GetLastError());
5899 goto done;
5902 service = OpenServiceW(scm, name,
5903 SERVICE_STOP |
5904 SERVICE_QUERY_STATUS |
5905 SERVICE_ENUMERATE_DEPENDENTS);
5906 if (!service)
5908 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
5909 goto done;
5912 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
5913 sizeof(SERVICE_STATUS_PROCESS), &needed))
5915 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
5916 goto done;
5919 if (ssp.dwCurrentState == SERVICE_STOPPED)
5920 goto done;
5922 stop_service_dependents(scm, service);
5924 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5925 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5927 done:
5928 CloseServiceHandle(service);
5929 CloseServiceHandle(scm);
5931 return ERROR_SUCCESS;
5934 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
5936 MSIPACKAGE *package = param;
5937 MSICOMPONENT *comp;
5938 MSIRECORD *uirow;
5939 LPCWSTR component;
5940 LPWSTR name = NULL, display_name = NULL;
5941 DWORD event, len;
5942 SC_HANDLE scm;
5944 event = MSI_RecordGetInteger( rec, 3 );
5945 if (!(event & msidbServiceControlEventStop))
5946 return ERROR_SUCCESS;
5948 component = MSI_RecordGetString( rec, 6 );
5949 comp = msi_get_loaded_component( package, component );
5950 if (!comp)
5951 return ERROR_SUCCESS;
5953 comp->Action = msi_get_component_action( package, comp );
5954 if (comp->Action != INSTALLSTATE_ABSENT)
5956 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
5957 return ERROR_SUCCESS;
5960 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
5961 if (!scm)
5963 ERR("Failed to open the service control manager\n");
5964 goto done;
5967 len = 0;
5968 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5969 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5971 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5972 GetServiceDisplayNameW( scm, name, display_name, &len );
5974 CloseServiceHandle( scm );
5976 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
5977 stop_service( name );
5979 done:
5980 uirow = MSI_CreateRecord( 2 );
5981 MSI_RecordSetStringW( uirow, 1, display_name );
5982 MSI_RecordSetStringW( uirow, 2, name );
5983 msi_ui_actiondata( package, szStopServices, uirow );
5984 msiobj_release( &uirow->hdr );
5986 msi_free( name );
5987 msi_free( display_name );
5988 return ERROR_SUCCESS;
5991 static UINT ACTION_StopServices( MSIPACKAGE *package )
5993 static const WCHAR query[] = {
5994 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5995 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
5996 MSIQUERY *view;
5997 UINT rc;
5999 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6000 if (rc != ERROR_SUCCESS)
6001 return ERROR_SUCCESS;
6003 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6004 msiobj_release(&view->hdr);
6005 return rc;
6008 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6010 MSIPACKAGE *package = param;
6011 MSICOMPONENT *comp;
6012 MSIRECORD *uirow;
6013 LPWSTR name = NULL, display_name = NULL;
6014 DWORD event, len;
6015 SC_HANDLE scm = NULL, service = NULL;
6017 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6018 if (!comp)
6019 return ERROR_SUCCESS;
6021 event = MSI_RecordGetInteger( rec, 3 );
6022 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6024 comp->Action = msi_get_component_action( package, comp );
6025 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6026 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6028 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6029 msi_free( name );
6030 return ERROR_SUCCESS;
6032 stop_service( name );
6034 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6035 if (!scm)
6037 WARN("Failed to open the SCM: %d\n", GetLastError());
6038 goto done;
6041 len = 0;
6042 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6043 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6045 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6046 GetServiceDisplayNameW( scm, name, display_name, &len );
6049 service = OpenServiceW( scm, name, DELETE );
6050 if (!service)
6052 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6053 goto done;
6056 if (!DeleteService( service ))
6057 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6059 done:
6060 uirow = MSI_CreateRecord( 2 );
6061 MSI_RecordSetStringW( uirow, 1, display_name );
6062 MSI_RecordSetStringW( uirow, 2, name );
6063 msi_ui_actiondata( package, szDeleteServices, uirow );
6064 msiobj_release( &uirow->hdr );
6066 CloseServiceHandle( service );
6067 CloseServiceHandle( scm );
6068 msi_free( name );
6069 msi_free( display_name );
6071 return ERROR_SUCCESS;
6074 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6076 static const WCHAR query[] = {
6077 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6078 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6079 MSIQUERY *view;
6080 UINT rc;
6082 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6083 if (rc != ERROR_SUCCESS)
6084 return ERROR_SUCCESS;
6086 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6087 msiobj_release( &view->hdr );
6088 return rc;
6091 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6093 MSIPACKAGE *package = param;
6094 LPWSTR driver, driver_path, ptr;
6095 WCHAR outpath[MAX_PATH];
6096 MSIFILE *driver_file = NULL, *setup_file = NULL;
6097 MSICOMPONENT *comp;
6098 MSIRECORD *uirow;
6099 LPCWSTR desc, file_key, component;
6100 DWORD len, usage;
6101 UINT r = ERROR_SUCCESS;
6103 static const WCHAR driver_fmt[] = {
6104 'D','r','i','v','e','r','=','%','s',0};
6105 static const WCHAR setup_fmt[] = {
6106 'S','e','t','u','p','=','%','s',0};
6107 static const WCHAR usage_fmt[] = {
6108 'F','i','l','e','U','s','a','g','e','=','1',0};
6110 component = MSI_RecordGetString( rec, 2 );
6111 comp = msi_get_loaded_component( package, component );
6112 if (!comp)
6113 return ERROR_SUCCESS;
6115 comp->Action = msi_get_component_action( package, comp );
6116 if (comp->Action != INSTALLSTATE_LOCAL)
6118 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6119 return ERROR_SUCCESS;
6121 desc = MSI_RecordGetString(rec, 3);
6123 file_key = MSI_RecordGetString( rec, 4 );
6124 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6126 file_key = MSI_RecordGetString( rec, 5 );
6127 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6129 if (!driver_file)
6131 ERR("ODBC Driver entry not found!\n");
6132 return ERROR_FUNCTION_FAILED;
6135 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6136 if (setup_file)
6137 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6138 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6140 driver = msi_alloc(len * sizeof(WCHAR));
6141 if (!driver)
6142 return ERROR_OUTOFMEMORY;
6144 ptr = driver;
6145 lstrcpyW(ptr, desc);
6146 ptr += lstrlenW(ptr) + 1;
6148 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6149 ptr += len + 1;
6151 if (setup_file)
6153 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6154 ptr += len + 1;
6157 lstrcpyW(ptr, usage_fmt);
6158 ptr += lstrlenW(ptr) + 1;
6159 *ptr = '\0';
6161 if (!driver_file->TargetPath)
6163 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6164 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6166 driver_path = strdupW(driver_file->TargetPath);
6167 ptr = strrchrW(driver_path, '\\');
6168 if (ptr) *ptr = '\0';
6170 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6171 NULL, ODBC_INSTALL_COMPLETE, &usage))
6173 ERR("Failed to install SQL driver!\n");
6174 r = ERROR_FUNCTION_FAILED;
6177 uirow = MSI_CreateRecord( 5 );
6178 MSI_RecordSetStringW( uirow, 1, desc );
6179 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6180 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6181 msi_ui_actiondata( package, szInstallODBC, uirow );
6182 msiobj_release( &uirow->hdr );
6184 msi_free(driver);
6185 msi_free(driver_path);
6187 return r;
6190 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6192 MSIPACKAGE *package = param;
6193 LPWSTR translator, translator_path, ptr;
6194 WCHAR outpath[MAX_PATH];
6195 MSIFILE *translator_file = NULL, *setup_file = NULL;
6196 MSICOMPONENT *comp;
6197 MSIRECORD *uirow;
6198 LPCWSTR desc, file_key, component;
6199 DWORD len, usage;
6200 UINT r = ERROR_SUCCESS;
6202 static const WCHAR translator_fmt[] = {
6203 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6204 static const WCHAR setup_fmt[] = {
6205 'S','e','t','u','p','=','%','s',0};
6207 component = MSI_RecordGetString( rec, 2 );
6208 comp = msi_get_loaded_component( package, component );
6209 if (!comp)
6210 return ERROR_SUCCESS;
6212 comp->Action = msi_get_component_action( package, comp );
6213 if (comp->Action != INSTALLSTATE_LOCAL)
6215 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6216 return ERROR_SUCCESS;
6218 desc = MSI_RecordGetString(rec, 3);
6220 file_key = MSI_RecordGetString( rec, 4 );
6221 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6223 file_key = MSI_RecordGetString( rec, 5 );
6224 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6226 if (!translator_file)
6228 ERR("ODBC Translator entry not found!\n");
6229 return ERROR_FUNCTION_FAILED;
6232 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6233 if (setup_file)
6234 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6236 translator = msi_alloc(len * sizeof(WCHAR));
6237 if (!translator)
6238 return ERROR_OUTOFMEMORY;
6240 ptr = translator;
6241 lstrcpyW(ptr, desc);
6242 ptr += lstrlenW(ptr) + 1;
6244 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6245 ptr += len + 1;
6247 if (setup_file)
6249 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6250 ptr += len + 1;
6252 *ptr = '\0';
6254 translator_path = strdupW(translator_file->TargetPath);
6255 ptr = strrchrW(translator_path, '\\');
6256 if (ptr) *ptr = '\0';
6258 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6259 NULL, ODBC_INSTALL_COMPLETE, &usage))
6261 ERR("Failed to install SQL translator!\n");
6262 r = ERROR_FUNCTION_FAILED;
6265 uirow = MSI_CreateRecord( 5 );
6266 MSI_RecordSetStringW( uirow, 1, desc );
6267 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6268 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6269 msi_ui_actiondata( package, szInstallODBC, uirow );
6270 msiobj_release( &uirow->hdr );
6272 msi_free(translator);
6273 msi_free(translator_path);
6275 return r;
6278 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6280 MSIPACKAGE *package = param;
6281 MSICOMPONENT *comp;
6282 LPWSTR attrs;
6283 LPCWSTR desc, driver, component;
6284 WORD request = ODBC_ADD_SYS_DSN;
6285 INT registration;
6286 DWORD len;
6287 UINT r = ERROR_SUCCESS;
6288 MSIRECORD *uirow;
6290 static const WCHAR attrs_fmt[] = {
6291 'D','S','N','=','%','s',0 };
6293 component = MSI_RecordGetString( rec, 2 );
6294 comp = msi_get_loaded_component( package, component );
6295 if (!comp)
6296 return ERROR_SUCCESS;
6298 comp->Action = msi_get_component_action( package, comp );
6299 if (comp->Action != INSTALLSTATE_LOCAL)
6301 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6302 return ERROR_SUCCESS;
6305 desc = MSI_RecordGetString(rec, 3);
6306 driver = MSI_RecordGetString(rec, 4);
6307 registration = MSI_RecordGetInteger(rec, 5);
6309 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6310 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6312 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6313 attrs = msi_alloc(len * sizeof(WCHAR));
6314 if (!attrs)
6315 return ERROR_OUTOFMEMORY;
6317 len = sprintfW(attrs, attrs_fmt, desc);
6318 attrs[len + 1] = 0;
6320 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6322 ERR("Failed to install SQL data source!\n");
6323 r = ERROR_FUNCTION_FAILED;
6326 uirow = MSI_CreateRecord( 5 );
6327 MSI_RecordSetStringW( uirow, 1, desc );
6328 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6329 MSI_RecordSetInteger( uirow, 3, request );
6330 msi_ui_actiondata( package, szInstallODBC, uirow );
6331 msiobj_release( &uirow->hdr );
6333 msi_free(attrs);
6335 return r;
6338 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6340 static const WCHAR driver_query[] = {
6341 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6342 'O','D','B','C','D','r','i','v','e','r',0};
6343 static const WCHAR translator_query[] = {
6344 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6345 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6346 static const WCHAR source_query[] = {
6347 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6348 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6349 MSIQUERY *view;
6350 UINT rc;
6352 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6353 if (rc == ERROR_SUCCESS)
6355 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6356 msiobj_release(&view->hdr);
6357 if (rc != ERROR_SUCCESS)
6358 return rc;
6360 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6361 if (rc == ERROR_SUCCESS)
6363 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6364 msiobj_release(&view->hdr);
6365 if (rc != ERROR_SUCCESS)
6366 return rc;
6368 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6369 if (rc == ERROR_SUCCESS)
6371 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6372 msiobj_release(&view->hdr);
6373 if (rc != ERROR_SUCCESS)
6374 return rc;
6376 return ERROR_SUCCESS;
6379 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6381 MSIPACKAGE *package = param;
6382 MSICOMPONENT *comp;
6383 MSIRECORD *uirow;
6384 DWORD usage;
6385 LPCWSTR desc, component;
6387 component = MSI_RecordGetString( rec, 2 );
6388 comp = msi_get_loaded_component( package, component );
6389 if (!comp)
6390 return ERROR_SUCCESS;
6392 comp->Action = msi_get_component_action( package, comp );
6393 if (comp->Action != INSTALLSTATE_ABSENT)
6395 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6396 return ERROR_SUCCESS;
6399 desc = MSI_RecordGetString( rec, 3 );
6400 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6402 WARN("Failed to remove ODBC driver\n");
6404 else if (!usage)
6406 FIXME("Usage count reached 0\n");
6409 uirow = MSI_CreateRecord( 2 );
6410 MSI_RecordSetStringW( uirow, 1, desc );
6411 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6412 msi_ui_actiondata( package, szRemoveODBC, uirow );
6413 msiobj_release( &uirow->hdr );
6415 return ERROR_SUCCESS;
6418 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6420 MSIPACKAGE *package = param;
6421 MSICOMPONENT *comp;
6422 MSIRECORD *uirow;
6423 DWORD usage;
6424 LPCWSTR desc, component;
6426 component = MSI_RecordGetString( rec, 2 );
6427 comp = msi_get_loaded_component( package, component );
6428 if (!comp)
6429 return ERROR_SUCCESS;
6431 comp->Action = msi_get_component_action( package, comp );
6432 if (comp->Action != INSTALLSTATE_ABSENT)
6434 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6435 return ERROR_SUCCESS;
6438 desc = MSI_RecordGetString( rec, 3 );
6439 if (!SQLRemoveTranslatorW( desc, &usage ))
6441 WARN("Failed to remove ODBC translator\n");
6443 else if (!usage)
6445 FIXME("Usage count reached 0\n");
6448 uirow = MSI_CreateRecord( 2 );
6449 MSI_RecordSetStringW( uirow, 1, desc );
6450 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6451 msi_ui_actiondata( package, szRemoveODBC, uirow );
6452 msiobj_release( &uirow->hdr );
6454 return ERROR_SUCCESS;
6457 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6459 MSIPACKAGE *package = param;
6460 MSICOMPONENT *comp;
6461 MSIRECORD *uirow;
6462 LPWSTR attrs;
6463 LPCWSTR desc, driver, component;
6464 WORD request = ODBC_REMOVE_SYS_DSN;
6465 INT registration;
6466 DWORD len;
6468 static const WCHAR attrs_fmt[] = {
6469 'D','S','N','=','%','s',0 };
6471 component = MSI_RecordGetString( rec, 2 );
6472 comp = msi_get_loaded_component( package, component );
6473 if (!comp)
6474 return ERROR_SUCCESS;
6476 comp->Action = msi_get_component_action( package, comp );
6477 if (comp->Action != INSTALLSTATE_ABSENT)
6479 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6480 return ERROR_SUCCESS;
6483 desc = MSI_RecordGetString( rec, 3 );
6484 driver = MSI_RecordGetString( rec, 4 );
6485 registration = MSI_RecordGetInteger( rec, 5 );
6487 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6488 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6490 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6491 attrs = msi_alloc( len * sizeof(WCHAR) );
6492 if (!attrs)
6493 return ERROR_OUTOFMEMORY;
6495 FIXME("Use ODBCSourceAttribute table\n");
6497 len = sprintfW( attrs, attrs_fmt, desc );
6498 attrs[len + 1] = 0;
6500 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6502 WARN("Failed to remove ODBC data source\n");
6504 msi_free( attrs );
6506 uirow = MSI_CreateRecord( 3 );
6507 MSI_RecordSetStringW( uirow, 1, desc );
6508 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6509 MSI_RecordSetInteger( uirow, 3, request );
6510 msi_ui_actiondata( package, szRemoveODBC, uirow );
6511 msiobj_release( &uirow->hdr );
6513 return ERROR_SUCCESS;
6516 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6518 static const WCHAR driver_query[] = {
6519 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6520 'O','D','B','C','D','r','i','v','e','r',0};
6521 static const WCHAR translator_query[] = {
6522 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6523 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6524 static const WCHAR source_query[] = {
6525 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6526 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6527 MSIQUERY *view;
6528 UINT rc;
6530 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6531 if (rc == ERROR_SUCCESS)
6533 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6534 msiobj_release( &view->hdr );
6535 if (rc != ERROR_SUCCESS)
6536 return rc;
6538 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6539 if (rc == ERROR_SUCCESS)
6541 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6542 msiobj_release( &view->hdr );
6543 if (rc != ERROR_SUCCESS)
6544 return rc;
6546 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6547 if (rc == ERROR_SUCCESS)
6549 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6550 msiobj_release( &view->hdr );
6551 if (rc != ERROR_SUCCESS)
6552 return rc;
6554 return ERROR_SUCCESS;
6557 #define ENV_ACT_SETALWAYS 0x1
6558 #define ENV_ACT_SETABSENT 0x2
6559 #define ENV_ACT_REMOVE 0x4
6560 #define ENV_ACT_REMOVEMATCH 0x8
6562 #define ENV_MOD_MACHINE 0x20000000
6563 #define ENV_MOD_APPEND 0x40000000
6564 #define ENV_MOD_PREFIX 0x80000000
6565 #define ENV_MOD_MASK 0xC0000000
6567 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6569 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6571 LPCWSTR cptr = *name;
6573 static const WCHAR prefix[] = {'[','~',']',0};
6574 static const int prefix_len = 3;
6576 *flags = 0;
6577 while (*cptr)
6579 if (*cptr == '=')
6580 *flags |= ENV_ACT_SETALWAYS;
6581 else if (*cptr == '+')
6582 *flags |= ENV_ACT_SETABSENT;
6583 else if (*cptr == '-')
6584 *flags |= ENV_ACT_REMOVE;
6585 else if (*cptr == '!')
6586 *flags |= ENV_ACT_REMOVEMATCH;
6587 else if (*cptr == '*')
6588 *flags |= ENV_MOD_MACHINE;
6589 else
6590 break;
6592 cptr++;
6593 (*name)++;
6596 if (!*cptr)
6598 ERR("Missing environment variable\n");
6599 return ERROR_FUNCTION_FAILED;
6602 if (*value)
6604 LPCWSTR ptr = *value;
6605 if (!strncmpW(ptr, prefix, prefix_len))
6607 if (ptr[prefix_len] == szSemiColon[0])
6609 *flags |= ENV_MOD_APPEND;
6610 *value += lstrlenW(prefix);
6612 else
6614 *value = NULL;
6617 else if (lstrlenW(*value) >= prefix_len)
6619 ptr += lstrlenW(ptr) - prefix_len;
6620 if (!strcmpW( ptr, prefix ))
6622 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6624 *flags |= ENV_MOD_PREFIX;
6625 /* the "[~]" will be removed by deformat_string */;
6627 else
6629 *value = NULL;
6635 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6636 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6637 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6638 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6640 ERR("Invalid flags: %08x\n", *flags);
6641 return ERROR_FUNCTION_FAILED;
6644 if (!*flags)
6645 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6647 return ERROR_SUCCESS;
6650 static UINT open_env_key( DWORD flags, HKEY *key )
6652 static const WCHAR user_env[] =
6653 {'E','n','v','i','r','o','n','m','e','n','t',0};
6654 static const WCHAR machine_env[] =
6655 {'S','y','s','t','e','m','\\',
6656 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6657 'C','o','n','t','r','o','l','\\',
6658 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6659 'E','n','v','i','r','o','n','m','e','n','t',0};
6660 const WCHAR *env;
6661 HKEY root;
6662 LONG res;
6664 if (flags & ENV_MOD_MACHINE)
6666 env = machine_env;
6667 root = HKEY_LOCAL_MACHINE;
6669 else
6671 env = user_env;
6672 root = HKEY_CURRENT_USER;
6675 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6676 if (res != ERROR_SUCCESS)
6678 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6679 return ERROR_FUNCTION_FAILED;
6682 return ERROR_SUCCESS;
6685 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6687 MSIPACKAGE *package = param;
6688 LPCWSTR name, value, component;
6689 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6690 DWORD flags, type, size;
6691 UINT res;
6692 HKEY env = NULL;
6693 MSICOMPONENT *comp;
6694 MSIRECORD *uirow;
6695 int action = 0;
6697 component = MSI_RecordGetString(rec, 4);
6698 comp = msi_get_loaded_component(package, component);
6699 if (!comp)
6700 return ERROR_SUCCESS;
6702 comp->Action = msi_get_component_action( package, comp );
6703 if (comp->Action != INSTALLSTATE_LOCAL)
6705 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6706 return ERROR_SUCCESS;
6708 name = MSI_RecordGetString(rec, 2);
6709 value = MSI_RecordGetString(rec, 3);
6711 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6713 res = env_parse_flags(&name, &value, &flags);
6714 if (res != ERROR_SUCCESS || !value)
6715 goto done;
6717 if (value && !deformat_string(package, value, &deformatted))
6719 res = ERROR_OUTOFMEMORY;
6720 goto done;
6723 value = deformatted;
6725 res = open_env_key( flags, &env );
6726 if (res != ERROR_SUCCESS)
6727 goto done;
6729 if (flags & ENV_MOD_MACHINE)
6730 action |= 0x20000000;
6732 size = 0;
6733 type = REG_SZ;
6734 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6735 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6736 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6737 goto done;
6739 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6741 action = 0x2;
6743 /* Nothing to do. */
6744 if (!value)
6746 res = ERROR_SUCCESS;
6747 goto done;
6750 /* If we are appending but the string was empty, strip ; */
6751 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6753 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6754 newval = strdupW(value);
6755 if (!newval)
6757 res = ERROR_OUTOFMEMORY;
6758 goto done;
6761 else
6763 action = 0x1;
6765 /* Contrary to MSDN, +-variable to [~];path works */
6766 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6768 res = ERROR_SUCCESS;
6769 goto done;
6772 data = msi_alloc(size);
6773 if (!data)
6775 RegCloseKey(env);
6776 return ERROR_OUTOFMEMORY;
6779 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
6780 if (res != ERROR_SUCCESS)
6781 goto done;
6783 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
6785 action = 0x4;
6786 res = RegDeleteValueW(env, name);
6787 if (res != ERROR_SUCCESS)
6788 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
6789 goto done;
6792 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
6793 if (flags & ENV_MOD_MASK)
6795 DWORD mod_size;
6796 int multiplier = 0;
6797 if (flags & ENV_MOD_APPEND) multiplier++;
6798 if (flags & ENV_MOD_PREFIX) multiplier++;
6799 mod_size = lstrlenW(value) * multiplier;
6800 size += mod_size * sizeof(WCHAR);
6803 newval = msi_alloc(size);
6804 ptr = newval;
6805 if (!newval)
6807 res = ERROR_OUTOFMEMORY;
6808 goto done;
6811 if (flags & ENV_MOD_PREFIX)
6813 lstrcpyW(newval, value);
6814 ptr = newval + lstrlenW(value);
6815 action |= 0x80000000;
6818 lstrcpyW(ptr, data);
6820 if (flags & ENV_MOD_APPEND)
6822 lstrcatW(newval, value);
6823 action |= 0x40000000;
6826 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
6827 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
6828 if (res)
6830 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
6833 done:
6834 uirow = MSI_CreateRecord( 3 );
6835 MSI_RecordSetStringW( uirow, 1, name );
6836 MSI_RecordSetStringW( uirow, 2, newval );
6837 MSI_RecordSetInteger( uirow, 3, action );
6838 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
6839 msiobj_release( &uirow->hdr );
6841 if (env) RegCloseKey(env);
6842 msi_free(deformatted);
6843 msi_free(data);
6844 msi_free(newval);
6845 return res;
6848 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
6850 static const WCHAR query[] = {
6851 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6852 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6853 MSIQUERY *view;
6854 UINT rc;
6856 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6857 if (rc != ERROR_SUCCESS)
6858 return ERROR_SUCCESS;
6860 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
6861 msiobj_release(&view->hdr);
6862 return rc;
6865 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
6867 MSIPACKAGE *package = param;
6868 LPCWSTR name, value, component;
6869 LPWSTR deformatted = NULL;
6870 DWORD flags;
6871 HKEY env;
6872 MSICOMPONENT *comp;
6873 MSIRECORD *uirow;
6874 int action = 0;
6875 LONG res;
6876 UINT r;
6878 component = MSI_RecordGetString( rec, 4 );
6879 comp = msi_get_loaded_component( package, component );
6880 if (!comp)
6881 return ERROR_SUCCESS;
6883 comp->Action = msi_get_component_action( package, comp );
6884 if (comp->Action != INSTALLSTATE_ABSENT)
6886 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6887 return ERROR_SUCCESS;
6889 name = MSI_RecordGetString( rec, 2 );
6890 value = MSI_RecordGetString( rec, 3 );
6892 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6894 r = env_parse_flags( &name, &value, &flags );
6895 if (r != ERROR_SUCCESS)
6896 return r;
6898 if (!(flags & ENV_ACT_REMOVE))
6900 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
6901 return ERROR_SUCCESS;
6904 if (value && !deformat_string( package, value, &deformatted ))
6905 return ERROR_OUTOFMEMORY;
6907 value = deformatted;
6909 r = open_env_key( flags, &env );
6910 if (r != ERROR_SUCCESS)
6912 r = ERROR_SUCCESS;
6913 goto done;
6916 if (flags & ENV_MOD_MACHINE)
6917 action |= 0x20000000;
6919 TRACE("Removing %s\n", debugstr_w(name));
6921 res = RegDeleteValueW( env, name );
6922 if (res != ERROR_SUCCESS)
6924 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
6925 r = ERROR_SUCCESS;
6928 done:
6929 uirow = MSI_CreateRecord( 3 );
6930 MSI_RecordSetStringW( uirow, 1, name );
6931 MSI_RecordSetStringW( uirow, 2, value );
6932 MSI_RecordSetInteger( uirow, 3, action );
6933 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
6934 msiobj_release( &uirow->hdr );
6936 if (env) RegCloseKey( env );
6937 msi_free( deformatted );
6938 return r;
6941 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6943 static const WCHAR query[] = {
6944 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6945 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
6946 MSIQUERY *view;
6947 UINT rc;
6949 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6950 if (rc != ERROR_SUCCESS)
6951 return ERROR_SUCCESS;
6953 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
6954 msiobj_release( &view->hdr );
6955 return rc;
6958 UINT msi_validate_product_id( MSIPACKAGE *package )
6960 LPWSTR key, template, id;
6961 UINT r = ERROR_SUCCESS;
6963 id = msi_dup_property( package->db, szProductID );
6964 if (id)
6966 msi_free( id );
6967 return ERROR_SUCCESS;
6969 template = msi_dup_property( package->db, szPIDTemplate );
6970 key = msi_dup_property( package->db, szPIDKEY );
6971 if (key && template)
6973 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
6974 r = msi_set_property( package->db, szProductID, key );
6976 msi_free( template );
6977 msi_free( key );
6978 return r;
6981 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6983 return msi_validate_product_id( package );
6986 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
6988 TRACE("\n");
6989 package->need_reboot_at_end = 1;
6990 return ERROR_SUCCESS;
6993 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6995 static const WCHAR szAvailableFreeReg[] =
6996 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
6997 MSIRECORD *uirow;
6998 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7000 TRACE("%p %d kilobytes\n", package, space);
7002 uirow = MSI_CreateRecord( 1 );
7003 MSI_RecordSetInteger( uirow, 1, space );
7004 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7005 msiobj_release( &uirow->hdr );
7007 return ERROR_SUCCESS;
7010 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7012 TRACE("%p\n", package);
7014 msi_set_property( package->db, szRollbackDisabled, szOne );
7015 return ERROR_SUCCESS;
7018 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7020 FIXME("%p\n", package);
7021 return ERROR_SUCCESS;
7024 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7026 static const WCHAR driver_query[] = {
7027 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7028 'O','D','B','C','D','r','i','v','e','r',0};
7029 static const WCHAR translator_query[] = {
7030 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7031 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7032 MSIQUERY *view;
7033 UINT r, count;
7035 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7036 if (r == ERROR_SUCCESS)
7038 count = 0;
7039 r = MSI_IterateRecords( view, &count, NULL, package );
7040 msiobj_release( &view->hdr );
7041 if (r != ERROR_SUCCESS)
7042 return r;
7043 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7045 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7046 if (r == ERROR_SUCCESS)
7048 count = 0;
7049 r = MSI_IterateRecords( view, &count, NULL, package );
7050 msiobj_release( &view->hdr );
7051 if (r != ERROR_SUCCESS)
7052 return r;
7053 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7055 return ERROR_SUCCESS;
7058 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7060 static const WCHAR fmtW[] =
7061 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7062 MSIPACKAGE *package = param;
7063 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7064 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7065 WCHAR *product, *features, *cmd;
7066 STARTUPINFOW si;
7067 PROCESS_INFORMATION info;
7068 BOOL ret;
7070 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7072 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7074 len += strlenW( product );
7075 if (features)
7076 len += strlenW( features );
7077 else
7078 len += sizeof(szAll) / sizeof(szAll[0]);
7080 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7082 msi_free( product );
7083 msi_free( features );
7084 return ERROR_OUTOFMEMORY;
7086 sprintfW( cmd, fmtW, product, features ? features : szAll );
7087 msi_free( product );
7088 msi_free( features );
7090 memset( &si, 0, sizeof(STARTUPINFOW) );
7091 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7092 msi_free( cmd );
7093 if (!ret) return GetLastError();
7094 CloseHandle( info.hThread );
7096 WaitForSingleObject( info.hProcess, INFINITE );
7097 CloseHandle( info.hProcess );
7098 return ERROR_SUCCESS;
7101 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7103 static const WCHAR query[] = {
7104 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7105 MSIQUERY *view;
7106 UINT r;
7108 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7109 if (r == ERROR_SUCCESS)
7111 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7112 msiobj_release( &view->hdr );
7113 if (r != ERROR_SUCCESS)
7114 return r;
7116 return ERROR_SUCCESS;
7119 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7121 MSIPACKAGE *package = param;
7122 int attributes = MSI_RecordGetInteger( rec, 5 );
7124 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7126 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7127 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7128 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7129 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7130 HKEY hkey;
7131 UINT r;
7133 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7135 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7136 if (r != ERROR_SUCCESS)
7137 return ERROR_SUCCESS;
7139 else
7141 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7142 if (r != ERROR_SUCCESS)
7143 return ERROR_SUCCESS;
7145 RegCloseKey( hkey );
7147 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7148 debugstr_w(upgrade_code), debugstr_w(version_min),
7149 debugstr_w(version_max), debugstr_w(language));
7151 return ERROR_SUCCESS;
7154 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7156 static const WCHAR query[] = {
7157 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7158 'U','p','g','r','a','d','e',0};
7159 MSIQUERY *view;
7160 UINT r;
7162 if (msi_get_property_int( package->db, szInstalled, 0 ))
7164 TRACE("product is installed, skipping action\n");
7165 return ERROR_SUCCESS;
7167 if (msi_get_property_int( package->db, szPreselected, 0 ))
7169 TRACE("Preselected property is set, not migrating feature states\n");
7170 return ERROR_SUCCESS;
7172 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7173 if (r == ERROR_SUCCESS)
7175 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7176 msiobj_release( &view->hdr );
7177 if (r != ERROR_SUCCESS)
7178 return r;
7180 return ERROR_SUCCESS;
7183 static void bind_image( const char *filename, const char *path )
7185 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7187 WARN("failed to bind image %u\n", GetLastError());
7191 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7193 UINT i;
7194 MSIFILE *file;
7195 MSIPACKAGE *package = param;
7196 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7197 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7198 char *filenameA, *pathA;
7199 WCHAR *pathW, **path_list;
7201 if (!(file = msi_get_loaded_file( package, key )))
7203 WARN("file %s not found\n", debugstr_w(key));
7204 return ERROR_SUCCESS;
7206 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7207 path_list = msi_split_string( paths, ';' );
7208 if (!path_list) bind_image( filenameA, NULL );
7209 else
7211 for (i = 0; path_list[i] && path_list[i][0]; i++)
7213 deformat_string( package, path_list[i], &pathW );
7214 if ((pathA = strdupWtoA( pathW )))
7216 bind_image( filenameA, pathA );
7217 msi_free( pathA );
7219 msi_free( pathW );
7222 msi_free( path_list );
7223 msi_free( filenameA );
7224 return ERROR_SUCCESS;
7227 static UINT ACTION_BindImage( MSIPACKAGE *package )
7229 static const WCHAR query[] = {
7230 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7231 'B','i','n','d','I','m','a','g','e',0};
7232 MSIQUERY *view;
7233 UINT r;
7235 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7236 if (r == ERROR_SUCCESS)
7238 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7239 msiobj_release( &view->hdr );
7240 if (r != ERROR_SUCCESS)
7241 return r;
7243 return ERROR_SUCCESS;
7246 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7248 static const WCHAR query[] = {
7249 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7250 MSIQUERY *view;
7251 DWORD count = 0;
7252 UINT r;
7254 r = MSI_OpenQuery( package->db, &view, query, table );
7255 if (r == ERROR_SUCCESS)
7257 r = MSI_IterateRecords(view, &count, NULL, package);
7258 msiobj_release(&view->hdr);
7259 if (r != ERROR_SUCCESS)
7260 return r;
7262 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7263 return ERROR_SUCCESS;
7266 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7268 static const WCHAR table[] = {
7269 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7270 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7273 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7275 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7276 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7279 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7281 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7282 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7285 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7287 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7288 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7291 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7293 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7294 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7297 static const struct
7299 const WCHAR *action;
7300 UINT (*handler)(MSIPACKAGE *);
7301 const WCHAR *action_rollback;
7303 StandardActions[] =
7305 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7306 { szAppSearch, ACTION_AppSearch, NULL },
7307 { szBindImage, ACTION_BindImage, NULL },
7308 { szCCPSearch, ACTION_CCPSearch, NULL },
7309 { szCostFinalize, ACTION_CostFinalize, NULL },
7310 { szCostInitialize, ACTION_CostInitialize, NULL },
7311 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7312 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7313 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7314 { szDisableRollback, ACTION_DisableRollback, NULL },
7315 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7316 { szExecuteAction, ACTION_ExecuteAction, NULL },
7317 { szFileCost, ACTION_FileCost, NULL },
7318 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7319 { szForceReboot, ACTION_ForceReboot, NULL },
7320 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7321 { szInstallExecute, ACTION_InstallExecute, NULL },
7322 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7323 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7324 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7325 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7326 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7327 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7328 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7329 { szInstallValidate, ACTION_InstallValidate, NULL },
7330 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7331 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7332 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7333 { szMoveFiles, ACTION_MoveFiles, NULL },
7334 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7335 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7336 { szPatchFiles, ACTION_PatchFiles, NULL },
7337 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7338 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7339 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7340 { szPublishProduct, ACTION_PublishProduct, NULL },
7341 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7342 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7343 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7344 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7345 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7346 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7347 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7348 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7349 { szRegisterUser, ACTION_RegisterUser, NULL },
7350 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7351 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7352 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7353 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7354 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7355 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7356 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7357 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7358 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7359 { szResolveSource, ACTION_ResolveSource, NULL },
7360 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7361 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7362 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7363 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7364 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7365 { szStartServices, ACTION_StartServices, szStopServices },
7366 { szStopServices, ACTION_StopServices, szStartServices },
7367 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7368 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7369 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7370 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7371 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7372 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7373 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7374 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7375 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7376 { szValidateProductID, ACTION_ValidateProductID, NULL },
7377 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7378 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7379 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7380 { NULL, NULL, NULL }
7383 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7385 BOOL ret = FALSE;
7386 UINT i;
7388 i = 0;
7389 while (StandardActions[i].action != NULL)
7391 if (!strcmpW( StandardActions[i].action, action ))
7393 ui_actionstart( package, action );
7394 if (StandardActions[i].handler)
7396 ui_actioninfo( package, action, TRUE, 0 );
7397 *rc = StandardActions[i].handler( package );
7398 ui_actioninfo( package, action, FALSE, *rc );
7400 if (StandardActions[i].action_rollback && !package->need_rollback)
7402 TRACE("scheduling rollback action\n");
7403 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7406 else
7408 FIXME("unhandled standard action %s\n", debugstr_w(action));
7409 *rc = ERROR_SUCCESS;
7411 ret = TRUE;
7412 break;
7414 i++;
7416 return ret;
7419 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7421 UINT rc = ERROR_SUCCESS;
7422 BOOL handled;
7424 TRACE("Performing action (%s)\n", debugstr_w(action));
7426 handled = ACTION_HandleStandardAction(package, action, &rc);
7428 if (!handled)
7429 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7431 if (!handled)
7433 WARN("unhandled msi action %s\n", debugstr_w(action));
7434 rc = ERROR_FUNCTION_NOT_CALLED;
7437 return rc;
7440 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7442 UINT rc = ERROR_SUCCESS;
7443 BOOL handled = FALSE;
7445 TRACE("Performing action (%s)\n", debugstr_w(action));
7447 package->action_progress_increment = 0;
7448 handled = ACTION_HandleStandardAction(package, action, &rc);
7450 if (!handled)
7451 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7453 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7454 handled = TRUE;
7456 if (!handled)
7458 WARN("unhandled msi action %s\n", debugstr_w(action));
7459 rc = ERROR_FUNCTION_NOT_CALLED;
7462 return rc;
7465 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7467 UINT rc = ERROR_SUCCESS;
7468 MSIRECORD *row;
7470 static const WCHAR query[] =
7471 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7472 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7473 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7474 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7475 static const WCHAR ui_query[] =
7476 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7477 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7478 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7479 ' ', '=',' ','%','i',0};
7481 if (needs_ui_sequence(package))
7482 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7483 else
7484 row = MSI_QueryGetRecord(package->db, query, seq);
7486 if (row)
7488 LPCWSTR action, cond;
7490 TRACE("Running the actions\n");
7492 /* check conditions */
7493 cond = MSI_RecordGetString(row, 2);
7495 /* this is a hack to skip errors in the condition code */
7496 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7498 msiobj_release(&row->hdr);
7499 return ERROR_SUCCESS;
7502 action = MSI_RecordGetString(row, 1);
7503 if (!action)
7505 ERR("failed to fetch action\n");
7506 msiobj_release(&row->hdr);
7507 return ERROR_FUNCTION_FAILED;
7510 if (needs_ui_sequence(package))
7511 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7512 else
7513 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7515 msiobj_release(&row->hdr);
7518 return rc;
7521 /****************************************************
7522 * TOP level entry points
7523 *****************************************************/
7525 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7526 LPCWSTR szCommandLine )
7528 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7529 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7530 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7531 WCHAR *reinstall = NULL;
7532 BOOL ui_exists;
7533 UINT rc;
7535 msi_set_property( package->db, szAction, szInstall );
7537 package->script->InWhatSequence = SEQUENCE_INSTALL;
7539 if (szPackagePath)
7541 LPWSTR p, dir;
7542 LPCWSTR file;
7544 dir = strdupW(szPackagePath);
7545 p = strrchrW(dir, '\\');
7546 if (p)
7548 *(++p) = 0;
7549 file = szPackagePath + (p - dir);
7551 else
7553 msi_free(dir);
7554 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7555 GetCurrentDirectoryW(MAX_PATH, dir);
7556 lstrcatW(dir, szBackSlash);
7557 file = szPackagePath;
7560 msi_free( package->PackagePath );
7561 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7562 if (!package->PackagePath)
7564 msi_free(dir);
7565 return ERROR_OUTOFMEMORY;
7568 lstrcpyW(package->PackagePath, dir);
7569 lstrcatW(package->PackagePath, file);
7570 msi_free(dir);
7572 msi_set_sourcedir_props(package, FALSE);
7575 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7576 if (rc != ERROR_SUCCESS)
7577 return rc;
7579 msi_apply_transforms( package );
7580 msi_apply_patches( package );
7582 if (!szCommandLine && msi_get_property_int( package->db, szInstalled, 0 ))
7584 TRACE("setting reinstall property\n");
7585 msi_set_property( package->db, szReinstall, szAll );
7588 /* properties may have been added by a transform */
7589 msi_clone_properties( package );
7591 msi_parse_command_line( package, szCommandLine, FALSE );
7592 msi_adjust_privilege_properties( package );
7593 msi_set_context( package );
7595 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7597 TRACE("disabling rollback\n");
7598 msi_set_property( package->db, szRollbackDisabled, szOne );
7601 if (needs_ui_sequence( package))
7603 package->script->InWhatSequence |= SEQUENCE_UI;
7604 rc = ACTION_ProcessUISequence(package);
7605 ui_exists = ui_sequence_exists(package);
7606 if (rc == ERROR_SUCCESS || !ui_exists)
7608 package->script->InWhatSequence |= SEQUENCE_EXEC;
7609 rc = ACTION_ProcessExecSequence(package, ui_exists);
7612 else
7613 rc = ACTION_ProcessExecSequence(package, FALSE);
7615 package->script->CurrentlyScripting = FALSE;
7617 /* process the ending type action */
7618 if (rc == ERROR_SUCCESS)
7619 ACTION_PerformActionSequence(package, -1);
7620 else if (rc == ERROR_INSTALL_USEREXIT)
7621 ACTION_PerformActionSequence(package, -2);
7622 else if (rc == ERROR_INSTALL_SUSPEND)
7623 ACTION_PerformActionSequence(package, -4);
7624 else /* failed */
7626 ACTION_PerformActionSequence(package, -3);
7627 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7629 package->need_rollback = TRUE;
7633 /* finish up running custom actions */
7634 ACTION_FinishCustomActions(package);
7636 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
7638 WARN("installation failed, running rollback script\n");
7639 execute_script( package, SCRIPT_ROLLBACK );
7641 msi_free( reinstall );
7643 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7644 return ERROR_SUCCESS_REBOOT_REQUIRED;
7646 return rc;