msi: Add support for ARPNOMODIFY, APRNOREMOVE and ARPNOREPAIR.
[wine.git] / dlls / msi / action.c
blobf2a038bc4190421abc10444bba759d061592b9d4
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 "winuser.h"
34 #include "shlobj.h"
35 #include "objbase.h"
36 #include "mscoree.h"
37 #include "shlwapi.h"
38 #include "imagehlp.h"
39 #include "wine/unicode.h"
40 #include "winver.h"
42 #include "msipriv.h"
43 #include "resource.h"
45 #define REG_PROGRESS_VALUE 13200
46 #define COMPONENT_PROGRESS_VALUE 24000
48 WINE_DEFAULT_DEBUG_CHANNEL(msi);
50 static const WCHAR szCreateFolders[] =
51 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
52 static const WCHAR szCostFinalize[] =
53 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
54 static const WCHAR szWriteRegistryValues[] =
55 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
56 static const WCHAR szFileCost[] =
57 {'F','i','l','e','C','o','s','t',0};
58 static const WCHAR szInstallInitialize[] =
59 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
60 static const WCHAR szInstallValidate[] =
61 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
62 static const WCHAR szLaunchConditions[] =
63 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
64 static const WCHAR szProcessComponents[] =
65 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
66 static const WCHAR szRegisterTypeLibraries[] =
67 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
68 static const WCHAR szCreateShortcuts[] =
69 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
70 static const WCHAR szPublishProduct[] =
71 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
72 static const WCHAR szWriteIniValues[] =
73 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
74 static const WCHAR szSelfRegModules[] =
75 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
76 static const WCHAR szPublishFeatures[] =
77 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
78 static const WCHAR szRegisterProduct[] =
79 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
80 static const WCHAR szInstallExecute[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
82 static const WCHAR szInstallExecuteAgain[] =
83 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
84 static const WCHAR szInstallFinalize[] =
85 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
86 static const WCHAR szForceReboot[] =
87 {'F','o','r','c','e','R','e','b','o','o','t',0};
88 static const WCHAR szResolveSource[] =
89 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
90 static const WCHAR szAllocateRegistrySpace[] =
91 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
92 static const WCHAR szBindImage[] =
93 {'B','i','n','d','I','m','a','g','e',0};
94 static const WCHAR szDeleteServices[] =
95 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
96 static const WCHAR szDisableRollback[] =
97 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
98 static const WCHAR szExecuteAction[] =
99 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
100 static const WCHAR szInstallAdminPackage[] =
101 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
102 static const WCHAR szInstallSFPCatalogFile[] =
103 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
104 static const WCHAR szIsolateComponents[] =
105 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
106 static const WCHAR szMigrateFeatureStates[] =
107 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','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 szUnpublishProduct[] =
149 {'U','n','p','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
150 static const WCHAR szUnregisterComPlus[] =
151 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
152 static const WCHAR szUnregisterTypeLibraries[] =
153 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
154 static const WCHAR szValidateProductID[] =
155 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
156 static const WCHAR szWriteEnvironmentStrings[] =
157 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
158 static const WCHAR szINSTALL[] =
159 {'I','N','S','T','A','L','L',0};
161 static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
163 WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
164 '`','A','c','t','i','o','n','T','e','x','t','`',' ','W','H','E','R','E',' ',
165 '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
166 MSIRECORD *row, *textrow;
167 INT rc;
169 textrow = MSI_QueryGetRecord(package->db, query, action);
170 if (textrow)
172 description = MSI_RecordGetString(textrow, 2);
173 template = MSI_RecordGetString(textrow, 3);
176 row = MSI_CreateRecord(3);
177 if (!row) return -1;
178 MSI_RecordSetStringW(row, 1, action);
179 MSI_RecordSetStringW(row, 2, description);
180 MSI_RecordSetStringW(row, 3, template);
181 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
182 if (textrow) msiobj_release(&textrow->hdr);
183 msiobj_release(&row->hdr);
184 return rc;
187 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
188 INT rc)
190 MSIRECORD *row;
191 WCHAR *template;
193 template = msi_get_error_message(package->db, start ? MSIERR_INFO_ACTIONSTART : MSIERR_INFO_ACTIONENDED);
195 row = MSI_CreateRecord(2);
196 if (!row)
198 msi_free(template);
199 return;
201 MSI_RecordSetStringW(row, 0, template);
202 MSI_RecordSetStringW(row, 1, action);
203 MSI_RecordSetInteger(row, 2, start ? package->LastActionResult : rc);
204 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
205 msiobj_release(&row->hdr);
206 msi_free(template);
207 if (!start) package->LastActionResult = rc;
210 enum parse_state
212 state_whitespace,
213 state_token,
214 state_quote
217 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
219 enum parse_state state = state_quote;
220 const WCHAR *p;
221 WCHAR *out = value;
222 BOOL ignore, in_quotes = FALSE;
223 int count = 0, len = 0;
225 for (p = str; *p; p++)
227 ignore = FALSE;
228 switch (state)
230 case state_whitespace:
231 switch (*p)
233 case ' ':
234 in_quotes = TRUE;
235 ignore = TRUE;
236 len++;
237 break;
238 case '"':
239 state = state_quote;
240 if (in_quotes && p[1] != '\"') count--;
241 else count++;
242 break;
243 default:
244 state = state_token;
245 in_quotes = TRUE;
246 len++;
247 break;
249 break;
251 case state_token:
252 switch (*p)
254 case '"':
255 state = state_quote;
256 if (in_quotes) count--;
257 else count++;
258 break;
259 case ' ':
260 state = state_whitespace;
261 if (!count) goto done;
262 in_quotes = TRUE;
263 len++;
264 break;
265 default:
266 if (count) in_quotes = TRUE;
267 len++;
268 break;
270 break;
272 case state_quote:
273 switch (*p)
275 case '"':
276 if (in_quotes && p[1] != '\"') count--;
277 else count++;
278 break;
279 case ' ':
280 state = state_whitespace;
281 if (!count || (count > 1 && !len)) goto done;
282 in_quotes = TRUE;
283 len++;
284 break;
285 default:
286 state = state_token;
287 if (count) in_quotes = TRUE;
288 len++;
289 break;
291 break;
293 default: break;
295 if (!ignore && value) *out++ = *p;
296 if (!count) in_quotes = FALSE;
299 done:
300 if (value)
302 if (!len) *value = 0;
303 else *out = 0;
306 if(quotes) *quotes = count;
307 return p - str;
310 static void remove_quotes( WCHAR *str )
312 WCHAR *p = str;
313 int len = strlenW( str );
315 while ((p = strchrW( p, '"' )))
317 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
318 p++;
322 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
323 BOOL preserve_case )
325 LPCWSTR ptr, ptr2;
326 int num_quotes;
327 DWORD len;
328 WCHAR *prop, *val;
329 UINT r;
331 if (!szCommandLine)
332 return ERROR_SUCCESS;
334 ptr = szCommandLine;
335 while (*ptr)
337 while (*ptr == ' ') ptr++;
338 if (!*ptr) break;
340 ptr2 = strchrW( ptr, '=' );
341 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
343 len = ptr2 - ptr;
344 if (!len) return ERROR_INVALID_COMMAND_LINE;
346 while (ptr[len - 1] == ' ') len--;
348 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
349 memcpy( prop, ptr, len * sizeof(WCHAR) );
350 prop[len] = 0;
351 if (!preserve_case) struprW( prop );
353 ptr2++;
354 while (*ptr2 == ' ') ptr2++;
356 num_quotes = 0;
357 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
358 len = parse_prop( ptr2, val, &num_quotes );
359 if (num_quotes % 2)
361 WARN("unbalanced quotes\n");
362 msi_free( val );
363 msi_free( prop );
364 return ERROR_INVALID_COMMAND_LINE;
366 remove_quotes( val );
367 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
369 r = msi_set_property( package->db, prop, val, -1 );
370 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
371 msi_reset_folders( package, TRUE );
373 msi_free( val );
374 msi_free( prop );
376 ptr = ptr2 + len;
379 return ERROR_SUCCESS;
382 const WCHAR *msi_get_command_line_option(const WCHAR *cmd, const WCHAR *option, UINT *len)
384 DWORD opt_len = strlenW(option);
386 if (!cmd)
387 return NULL;
389 while (*cmd)
391 BOOL found = FALSE;
393 while (*cmd == ' ') cmd++;
394 if (!*cmd) break;
396 if(!strncmpiW(cmd, option, opt_len))
397 found = TRUE;
399 cmd = strchrW( cmd, '=' );
400 if(!cmd) break;
401 cmd++;
402 while (*cmd == ' ') cmd++;
403 if (!*cmd) break;
405 *len = parse_prop( cmd, NULL, NULL);
406 if (found) return cmd;
407 cmd += *len;
410 return NULL;
413 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
415 LPCWSTR pc;
416 LPWSTR p, *ret = NULL;
417 UINT count = 0;
419 if (!str)
420 return ret;
422 /* count the number of substrings */
423 for ( pc = str, count = 0; pc; count++ )
425 pc = strchrW( pc, sep );
426 if (pc)
427 pc++;
430 /* allocate space for an array of substring pointers and the substrings */
431 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
432 (lstrlenW(str)+1) * sizeof(WCHAR) );
433 if (!ret)
434 return ret;
436 /* copy the string and set the pointers */
437 p = (LPWSTR) &ret[count+1];
438 lstrcpyW( p, str );
439 for( count = 0; (ret[count] = p); count++ )
441 p = strchrW( p, sep );
442 if (p)
443 *p++ = 0;
446 return ret;
449 static BOOL ui_sequence_exists( MSIPACKAGE *package )
451 static const WCHAR query [] = {
452 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
453 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
454 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',0};
455 MSIQUERY *view;
456 DWORD count = 0;
458 if (!(MSI_DatabaseOpenViewW( package->db, query, &view )))
460 MSI_IterateRecords( view, &count, NULL, package );
461 msiobj_release( &view->hdr );
463 return count != 0;
466 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
468 WCHAR *source, *check, *p, *db;
469 DWORD len;
471 if (!(db = msi_dup_property( package->db, szOriginalDatabase )))
472 return ERROR_OUTOFMEMORY;
474 if (!(p = strrchrW( db, '\\' )) && !(p = strrchrW( db, '/' )))
476 msi_free(db);
477 return ERROR_SUCCESS;
479 len = p - db + 2;
480 source = msi_alloc( len * sizeof(WCHAR) );
481 lstrcpynW( source, db, len );
482 msi_free( db );
484 check = msi_dup_property( package->db, szSourceDir );
485 if (!check || replace)
487 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
488 if (r == ERROR_SUCCESS)
489 msi_reset_folders( package, TRUE );
491 msi_free( check );
493 check = msi_dup_property( package->db, szSOURCEDIR );
494 if (!check || replace)
495 msi_set_property( package->db, szSOURCEDIR, source, -1 );
497 msi_free( check );
498 msi_free( source );
500 return ERROR_SUCCESS;
503 static BOOL needs_ui_sequence(MSIPACKAGE *package)
505 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
508 UINT msi_set_context(MSIPACKAGE *package)
510 UINT r = msi_locate_product( package->ProductCode, &package->Context );
511 if (r != ERROR_SUCCESS)
513 int num = msi_get_property_int( package->db, szAllUsers, 0 );
514 if (num == 1 || num == 2)
515 package->Context = MSIINSTALLCONTEXT_MACHINE;
516 else
517 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
519 return ERROR_SUCCESS;
522 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
524 UINT rc;
525 LPCWSTR cond, action;
526 MSIPACKAGE *package = param;
528 action = MSI_RecordGetString(row,1);
529 if (!action)
531 ERR("Error is retrieving action name\n");
532 return ERROR_FUNCTION_FAILED;
535 /* check conditions */
536 cond = MSI_RecordGetString(row,2);
538 /* this is a hack to skip errors in the condition code */
539 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
541 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
542 return ERROR_SUCCESS;
545 rc = ACTION_PerformAction(package, action);
547 msi_dialog_check_messages( NULL );
549 if (rc == ERROR_FUNCTION_NOT_CALLED)
550 rc = ERROR_SUCCESS;
552 if (rc != ERROR_SUCCESS)
553 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
555 if (package->need_reboot_now)
557 TRACE("action %s asked for immediate reboot, suspending installation\n",
558 debugstr_w(action));
559 rc = ACTION_ForceReboot( package );
561 return rc;
564 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
566 static const WCHAR query[] = {
567 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
568 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
569 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
570 '`','S','e','q','u','e','n','c','e','`',0};
571 MSIQUERY *view;
572 UINT r;
574 TRACE("%p %s\n", package, debugstr_w(table));
576 r = MSI_OpenQuery( package->db, &view, query, table );
577 if (r == ERROR_SUCCESS)
579 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
580 msiobj_release(&view->hdr);
582 return r;
585 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package)
587 static const WCHAR query[] = {
588 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
589 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
590 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
591 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','0',' ',
592 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
593 MSIQUERY *view;
594 UINT rc;
596 if (package->ExecuteSequenceRun)
598 TRACE("Execute Sequence already Run\n");
599 return ERROR_SUCCESS;
602 package->ExecuteSequenceRun = TRUE;
604 rc = MSI_OpenQuery(package->db, &view, query);
605 if (rc == ERROR_SUCCESS)
607 TRACE("Running the actions\n");
609 msi_set_property( package->db, szSourceDir, NULL, -1 );
610 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
611 msiobj_release(&view->hdr);
613 return rc;
616 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
618 static const WCHAR query[] = {
619 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
620 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
621 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
622 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
623 MSIQUERY *view;
624 UINT rc;
626 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
627 if (rc == ERROR_SUCCESS)
629 TRACE("Running the actions\n");
630 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
631 msiobj_release(&view->hdr);
633 return rc;
636 /********************************************************
637 * ACTION helper functions and functions that perform the actions
638 *******************************************************/
639 static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action)
641 UINT arc;
642 INT uirc;
644 uirc = ui_actionstart(package, action, NULL, NULL);
645 if (uirc == IDCANCEL)
646 return ERROR_INSTALL_USEREXIT;
647 ui_actioninfo(package, action, TRUE, 0);
648 arc = ACTION_CustomAction(package, action);
649 uirc = !arc;
651 if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
653 uirc = ACTION_ShowDialog(package, action);
654 switch (uirc)
656 case -1:
657 return ERROR_SUCCESS; /* stop immediately */
658 case 0: arc = ERROR_FUNCTION_NOT_CALLED; break;
659 case 1: arc = ERROR_SUCCESS; break;
660 case 2: arc = ERROR_INSTALL_USEREXIT; break;
661 case 3: arc = ERROR_INSTALL_FAILURE; break;
662 case 4: arc = ERROR_INSTALL_SUSPEND; break;
663 case 5: arc = ERROR_MORE_DATA; break;
664 case 6: arc = ERROR_INVALID_HANDLE_STATE; break;
665 case 7: arc = ERROR_INVALID_DATA; break;
666 case 8: arc = ERROR_INSTALL_ALREADY_RUNNING; break;
667 case 9: arc = ERROR_INSTALL_PACKAGE_REJECTED; break;
668 default: arc = ERROR_FUNCTION_FAILED; break;
672 ui_actioninfo(package, action, FALSE, uirc);
674 return arc;
677 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
679 MSICOMPONENT *comp;
681 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
683 if (!strcmpW( Component, comp->Component )) return comp;
685 return NULL;
688 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
690 MSIFEATURE *feature;
692 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
694 if (!strcmpW( Feature, feature->Feature )) return feature;
696 return NULL;
699 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
701 MSIFILE *file;
703 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
705 if (!strcmpW( key, file->File )) return file;
707 return NULL;
710 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
712 MSIFOLDER *folder;
714 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
716 if (!strcmpW( dir, folder->Directory )) return folder;
718 return NULL;
722 * Recursively create all directories in the path.
723 * shamelessly stolen from setupapi/queue.c
725 BOOL msi_create_full_path( const WCHAR *path )
727 BOOL ret = TRUE;
728 WCHAR *new_path;
729 int len;
731 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
732 strcpyW( new_path, path );
734 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
735 new_path[len - 1] = 0;
737 while (!CreateDirectoryW( new_path, NULL ))
739 WCHAR *slash;
740 DWORD last_error = GetLastError();
741 if (last_error == ERROR_ALREADY_EXISTS) break;
742 if (last_error != ERROR_PATH_NOT_FOUND)
744 ret = FALSE;
745 break;
747 if (!(slash = strrchrW( new_path, '\\' )))
749 ret = FALSE;
750 break;
752 len = slash - new_path;
753 new_path[len] = 0;
754 if (!msi_create_full_path( new_path ))
756 ret = FALSE;
757 break;
759 new_path[len] = '\\';
761 msi_free( new_path );
762 return ret;
765 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
767 MSIRECORD *row;
769 row = MSI_CreateRecord( 4 );
770 MSI_RecordSetInteger( row, 1, a );
771 MSI_RecordSetInteger( row, 2, b );
772 MSI_RecordSetInteger( row, 3, c );
773 MSI_RecordSetInteger( row, 4, d );
774 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
775 msiobj_release( &row->hdr );
777 msi_dialog_check_messages( NULL );
780 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
782 if (!comp->Enabled)
784 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
785 return INSTALLSTATE_UNKNOWN;
787 if (package->need_rollback) return comp->Installed;
788 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
790 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
791 return INSTALLSTATE_UNKNOWN;
793 return comp->ActionRequest;
796 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
798 if (package->need_rollback) return feature->Installed;
799 return feature->ActionRequest;
802 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
804 MSIPACKAGE *package = param;
805 LPCWSTR dir, component, full_path;
806 MSIRECORD *uirow;
807 MSIFOLDER *folder;
808 MSICOMPONENT *comp;
810 component = MSI_RecordGetString(row, 2);
811 if (!component)
812 return ERROR_SUCCESS;
814 comp = msi_get_loaded_component(package, component);
815 if (!comp)
816 return ERROR_SUCCESS;
818 comp->Action = msi_get_component_action( package, comp );
819 if (comp->Action != INSTALLSTATE_LOCAL)
821 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
822 return ERROR_SUCCESS;
825 dir = MSI_RecordGetString(row,1);
826 if (!dir)
828 ERR("Unable to get folder id\n");
829 return ERROR_SUCCESS;
832 uirow = MSI_CreateRecord(1);
833 MSI_RecordSetStringW(uirow, 1, dir);
834 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
835 msiobj_release(&uirow->hdr);
837 full_path = msi_get_target_folder( package, dir );
838 if (!full_path)
840 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
841 return ERROR_SUCCESS;
843 TRACE("folder is %s\n", debugstr_w(full_path));
845 folder = msi_get_loaded_folder( package, dir );
846 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
847 folder->State = FOLDER_STATE_CREATED;
848 return ERROR_SUCCESS;
851 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
853 static const WCHAR query[] = {
854 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
855 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
856 MSIQUERY *view;
857 UINT rc;
859 if (package->script == SCRIPT_NONE)
860 return msi_schedule_action(package, SCRIPT_INSTALL, szCreateFolders);
862 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
863 if (rc != ERROR_SUCCESS)
864 return ERROR_SUCCESS;
866 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
867 msiobj_release(&view->hdr);
868 return rc;
871 static void remove_persistent_folder( MSIFOLDER *folder )
873 FolderList *fl;
875 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
877 remove_persistent_folder( fl->folder );
879 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
881 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
885 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
887 MSIPACKAGE *package = param;
888 LPCWSTR dir, component, full_path;
889 MSIRECORD *uirow;
890 MSIFOLDER *folder;
891 MSICOMPONENT *comp;
893 component = MSI_RecordGetString(row, 2);
894 if (!component)
895 return ERROR_SUCCESS;
897 comp = msi_get_loaded_component(package, component);
898 if (!comp)
899 return ERROR_SUCCESS;
901 comp->Action = msi_get_component_action( package, comp );
902 if (comp->Action != INSTALLSTATE_ABSENT)
904 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
905 return ERROR_SUCCESS;
908 dir = MSI_RecordGetString( row, 1 );
909 if (!dir)
911 ERR("Unable to get folder id\n");
912 return ERROR_SUCCESS;
915 full_path = msi_get_target_folder( package, dir );
916 if (!full_path)
918 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
919 return ERROR_SUCCESS;
921 TRACE("folder is %s\n", debugstr_w(full_path));
923 uirow = MSI_CreateRecord( 1 );
924 MSI_RecordSetStringW( uirow, 1, dir );
925 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
926 msiobj_release( &uirow->hdr );
928 folder = msi_get_loaded_folder( package, dir );
929 remove_persistent_folder( folder );
930 return ERROR_SUCCESS;
933 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
935 static const WCHAR query[] = {
936 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
937 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
938 MSIQUERY *view;
939 UINT rc;
941 if (package->script == SCRIPT_NONE)
942 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveFolders);
944 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
945 if (rc != ERROR_SUCCESS)
946 return ERROR_SUCCESS;
948 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
949 msiobj_release( &view->hdr );
950 return rc;
953 static UINT load_component( MSIRECORD *row, LPVOID param )
955 MSIPACKAGE *package = param;
956 MSICOMPONENT *comp;
958 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
959 if (!comp)
960 return ERROR_FUNCTION_FAILED;
962 list_add_tail( &package->components, &comp->entry );
964 /* fill in the data */
965 comp->Component = msi_dup_record_field( row, 1 );
967 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
969 comp->ComponentId = msi_dup_record_field( row, 2 );
970 comp->Directory = msi_dup_record_field( row, 3 );
971 comp->Attributes = MSI_RecordGetInteger(row,4);
972 comp->Condition = msi_dup_record_field( row, 5 );
973 comp->KeyPath = msi_dup_record_field( row, 6 );
975 comp->Installed = INSTALLSTATE_UNKNOWN;
976 comp->Action = INSTALLSTATE_UNKNOWN;
977 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
979 comp->assembly = msi_load_assembly( package, comp );
980 return ERROR_SUCCESS;
983 UINT msi_load_all_components( MSIPACKAGE *package )
985 static const WCHAR query[] = {
986 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
987 '`','C','o','m','p','o','n','e','n','t','`',0};
988 MSIQUERY *view;
989 UINT r;
991 if (!list_empty(&package->components))
992 return ERROR_SUCCESS;
994 r = MSI_DatabaseOpenViewW( package->db, query, &view );
995 if (r != ERROR_SUCCESS)
996 return r;
998 if (!msi_init_assembly_caches( package ))
1000 ERR("can't initialize assembly caches\n");
1001 msiobj_release( &view->hdr );
1002 return ERROR_FUNCTION_FAILED;
1005 r = MSI_IterateRecords(view, NULL, load_component, package);
1006 msiobj_release(&view->hdr);
1007 return r;
1010 typedef struct {
1011 MSIPACKAGE *package;
1012 MSIFEATURE *feature;
1013 } _ilfs;
1015 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1017 ComponentList *cl;
1019 cl = msi_alloc( sizeof (*cl) );
1020 if ( !cl )
1021 return ERROR_NOT_ENOUGH_MEMORY;
1022 cl->component = comp;
1023 list_add_tail( &feature->Components, &cl->entry );
1025 return ERROR_SUCCESS;
1028 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1030 FeatureList *fl;
1032 fl = msi_alloc( sizeof(*fl) );
1033 if ( !fl )
1034 return ERROR_NOT_ENOUGH_MEMORY;
1035 fl->feature = child;
1036 list_add_tail( &parent->Children, &fl->entry );
1038 return ERROR_SUCCESS;
1041 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1043 _ilfs* ilfs = param;
1044 LPCWSTR component;
1045 MSICOMPONENT *comp;
1047 component = MSI_RecordGetString(row,1);
1049 /* check to see if the component is already loaded */
1050 comp = msi_get_loaded_component( ilfs->package, component );
1051 if (!comp)
1053 WARN("ignoring unknown component %s\n", debugstr_w(component));
1054 return ERROR_SUCCESS;
1056 add_feature_component( ilfs->feature, comp );
1057 comp->Enabled = TRUE;
1059 return ERROR_SUCCESS;
1062 static UINT load_feature(MSIRECORD * row, LPVOID param)
1064 static const WCHAR query[] = {
1065 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1066 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1067 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1068 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1069 MSIPACKAGE *package = param;
1070 MSIFEATURE *feature;
1071 MSIQUERY *view;
1072 _ilfs ilfs;
1073 UINT rc;
1075 /* fill in the data */
1077 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1078 if (!feature)
1079 return ERROR_NOT_ENOUGH_MEMORY;
1081 list_init( &feature->Children );
1082 list_init( &feature->Components );
1084 feature->Feature = msi_dup_record_field( row, 1 );
1086 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1088 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1089 feature->Title = msi_dup_record_field( row, 3 );
1090 feature->Description = msi_dup_record_field( row, 4 );
1092 if (!MSI_RecordIsNull(row,5))
1093 feature->Display = MSI_RecordGetInteger(row,5);
1095 feature->Level= MSI_RecordGetInteger(row,6);
1096 feature->Directory = msi_dup_record_field( row, 7 );
1097 feature->Attributes = MSI_RecordGetInteger(row,8);
1099 feature->Installed = INSTALLSTATE_UNKNOWN;
1100 feature->Action = INSTALLSTATE_UNKNOWN;
1101 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1103 list_add_tail( &package->features, &feature->entry );
1105 /* load feature components */
1107 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1108 if (rc != ERROR_SUCCESS)
1109 return ERROR_SUCCESS;
1111 ilfs.package = package;
1112 ilfs.feature = feature;
1114 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1115 msiobj_release(&view->hdr);
1116 return rc;
1119 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1121 MSIPACKAGE *package = param;
1122 MSIFEATURE *parent, *child;
1124 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1125 if (!child)
1126 return ERROR_FUNCTION_FAILED;
1128 if (!child->Feature_Parent)
1129 return ERROR_SUCCESS;
1131 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1132 if (!parent)
1133 return ERROR_FUNCTION_FAILED;
1135 add_feature_child( parent, child );
1136 return ERROR_SUCCESS;
1139 UINT msi_load_all_features( MSIPACKAGE *package )
1141 static const WCHAR query[] = {
1142 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1143 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1144 '`','D','i','s','p','l','a','y','`',0};
1145 MSIQUERY *view;
1146 UINT r;
1148 if (!list_empty(&package->features))
1149 return ERROR_SUCCESS;
1151 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1152 if (r != ERROR_SUCCESS)
1153 return r;
1155 r = MSI_IterateRecords( view, NULL, load_feature, package );
1156 if (r != ERROR_SUCCESS)
1158 msiobj_release( &view->hdr );
1159 return r;
1161 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1162 msiobj_release( &view->hdr );
1163 return r;
1166 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1168 if (!p)
1169 return p;
1170 p = strchrW(p, ch);
1171 if (!p)
1172 return p;
1173 *p = 0;
1174 return p+1;
1177 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1179 static const WCHAR query[] = {
1180 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1181 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1182 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1183 MSIQUERY *view = NULL;
1184 MSIRECORD *row = NULL;
1185 UINT r;
1187 TRACE("%s\n", debugstr_w(file->File));
1189 r = MSI_OpenQuery(package->db, &view, query, file->File);
1190 if (r != ERROR_SUCCESS)
1191 goto done;
1193 r = MSI_ViewExecute(view, NULL);
1194 if (r != ERROR_SUCCESS)
1195 goto done;
1197 r = MSI_ViewFetch(view, &row);
1198 if (r != ERROR_SUCCESS)
1199 goto done;
1201 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1202 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1203 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1204 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1205 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1207 done:
1208 if (view) msiobj_release(&view->hdr);
1209 if (row) msiobj_release(&row->hdr);
1210 return r;
1213 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1215 MSIRECORD *row;
1216 static const WCHAR query[] = {
1217 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1218 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1219 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1221 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1222 if (!row)
1224 WARN("query failed\n");
1225 return ERROR_FUNCTION_FAILED;
1228 file->disk_id = MSI_RecordGetInteger( row, 1 );
1229 msiobj_release( &row->hdr );
1230 return ERROR_SUCCESS;
1233 static UINT load_file(MSIRECORD *row, LPVOID param)
1235 MSIPACKAGE* package = param;
1236 LPCWSTR component;
1237 MSIFILE *file;
1239 /* fill in the data */
1241 file = msi_alloc_zero( sizeof (MSIFILE) );
1242 if (!file)
1243 return ERROR_NOT_ENOUGH_MEMORY;
1245 file->File = msi_dup_record_field( row, 1 );
1247 component = MSI_RecordGetString( row, 2 );
1248 file->Component = msi_get_loaded_component( package, component );
1250 if (!file->Component)
1252 WARN("Component not found: %s\n", debugstr_w(component));
1253 msi_free(file->File);
1254 msi_free(file);
1255 return ERROR_SUCCESS;
1258 file->FileName = msi_dup_record_field( row, 3 );
1259 msi_reduce_to_long_filename( file->FileName );
1261 file->ShortName = msi_dup_record_field( row, 3 );
1262 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1264 file->FileSize = MSI_RecordGetInteger( row, 4 );
1265 file->Version = msi_dup_record_field( row, 5 );
1266 file->Language = msi_dup_record_field( row, 6 );
1267 file->Attributes = MSI_RecordGetInteger( row, 7 );
1268 file->Sequence = MSI_RecordGetInteger( row, 8 );
1270 file->state = msifs_invalid;
1272 /* if the compressed bits are not set in the file attributes,
1273 * then read the information from the package word count property
1275 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1277 file->IsCompressed = FALSE;
1279 else if (file->Attributes &
1280 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1282 file->IsCompressed = TRUE;
1284 else if (file->Attributes & msidbFileAttributesNoncompressed)
1286 file->IsCompressed = FALSE;
1288 else
1290 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1293 load_file_hash(package, file);
1294 load_file_disk_id(package, file);
1296 TRACE("File loaded (file %s sequence %u)\n", debugstr_w(file->File), file->Sequence);
1298 list_add_tail( &package->files, &file->entry );
1300 return ERROR_SUCCESS;
1303 static UINT load_all_files(MSIPACKAGE *package)
1305 static const WCHAR query[] = {
1306 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1307 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1308 '`','S','e','q','u','e','n','c','e','`', 0};
1309 MSIQUERY *view;
1310 UINT rc;
1312 if (!list_empty(&package->files))
1313 return ERROR_SUCCESS;
1315 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1316 if (rc != ERROR_SUCCESS)
1317 return ERROR_SUCCESS;
1319 rc = MSI_IterateRecords(view, NULL, load_file, package);
1320 msiobj_release(&view->hdr);
1321 return rc;
1324 static UINT load_media( MSIRECORD *row, LPVOID param )
1326 MSIPACKAGE *package = param;
1327 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1328 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1330 /* FIXME: load external cabinets and directory sources too */
1331 if (!cabinet || cabinet[0] != '#' || disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
1332 return ERROR_SUCCESS;
1334 return msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1337 static UINT load_all_media( MSIPACKAGE *package )
1339 static const WCHAR query[] = {
1340 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1341 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1342 '`','D','i','s','k','I','d','`',0};
1343 MSIQUERY *view;
1344 UINT r;
1346 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1347 if (r != ERROR_SUCCESS)
1348 return ERROR_SUCCESS;
1350 r = MSI_IterateRecords( view, NULL, load_media, package );
1351 msiobj_release( &view->hdr );
1352 return r;
1355 static UINT load_patch_disk_id( MSIPACKAGE *package, MSIFILEPATCH *patch )
1357 static const WCHAR query[] =
1358 {'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1359 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1360 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','u',0};
1361 MSIRECORD *rec;
1363 if (!(rec = MSI_QueryGetRecord( package->db, query, patch->Sequence )))
1365 WARN("query failed\n");
1366 return ERROR_FUNCTION_FAILED;
1369 patch->disk_id = MSI_RecordGetInteger( rec, 1 );
1370 msiobj_release( &rec->hdr );
1371 return ERROR_SUCCESS;
1374 static UINT load_patch(MSIRECORD *row, LPVOID param)
1376 MSIPACKAGE *package = param;
1377 MSIFILEPATCH *patch;
1378 const WCHAR *file_key;
1380 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1381 if (!patch)
1382 return ERROR_NOT_ENOUGH_MEMORY;
1384 file_key = MSI_RecordGetString( row, 1 );
1385 patch->File = msi_get_loaded_file( package, file_key );
1386 if (!patch->File)
1388 ERR("Failed to find target for patch in File table\n");
1389 msi_free(patch);
1390 return ERROR_FUNCTION_FAILED;
1393 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1394 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1395 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1397 /* FIXME:
1398 * Header field - for patch validation.
1399 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1402 load_patch_disk_id( package, patch );
1404 TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence);
1406 list_add_tail( &package->filepatches, &patch->entry );
1408 return ERROR_SUCCESS;
1411 static UINT load_all_patches(MSIPACKAGE *package)
1413 static const WCHAR query[] = {
1414 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1415 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1416 '`','S','e','q','u','e','n','c','e','`',0};
1417 MSIQUERY *view;
1418 UINT rc;
1420 if (!list_empty(&package->filepatches))
1421 return ERROR_SUCCESS;
1423 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1424 if (rc != ERROR_SUCCESS)
1425 return ERROR_SUCCESS;
1427 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1428 msiobj_release(&view->hdr);
1429 return rc;
1432 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1434 static const WCHAR query[] = {
1435 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1436 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1437 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1438 MSIQUERY *view;
1440 folder->persistent = FALSE;
1441 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1443 if (!MSI_ViewExecute( view, NULL ))
1445 MSIRECORD *rec;
1446 if (!MSI_ViewFetch( view, &rec ))
1448 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1449 folder->persistent = TRUE;
1450 msiobj_release( &rec->hdr );
1453 msiobj_release( &view->hdr );
1455 return ERROR_SUCCESS;
1458 static UINT load_folder( MSIRECORD *row, LPVOID param )
1460 MSIPACKAGE *package = param;
1461 static WCHAR szEmpty[] = { 0 };
1462 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1463 MSIFOLDER *folder;
1465 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1466 list_init( &folder->children );
1467 folder->Directory = msi_dup_record_field( row, 1 );
1468 folder->Parent = msi_dup_record_field( row, 2 );
1469 p = msi_dup_record_field(row, 3);
1471 TRACE("%s\n", debugstr_w(folder->Directory));
1473 /* split src and target dir */
1474 tgt_short = p;
1475 src_short = folder_split_path( p, ':' );
1477 /* split the long and short paths */
1478 tgt_long = folder_split_path( tgt_short, '|' );
1479 src_long = folder_split_path( src_short, '|' );
1481 /* check for no-op dirs */
1482 if (tgt_short && !strcmpW( szDot, tgt_short ))
1483 tgt_short = szEmpty;
1484 if (src_short && !strcmpW( szDot, src_short ))
1485 src_short = szEmpty;
1487 if (!tgt_long)
1488 tgt_long = tgt_short;
1490 if (!src_short) {
1491 src_short = tgt_short;
1492 src_long = tgt_long;
1495 if (!src_long)
1496 src_long = src_short;
1498 /* FIXME: use the target short path too */
1499 folder->TargetDefault = strdupW(tgt_long);
1500 folder->SourceShortPath = strdupW(src_short);
1501 folder->SourceLongPath = strdupW(src_long);
1502 msi_free(p);
1504 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1505 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1506 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1508 load_folder_persistence( package, folder );
1510 list_add_tail( &package->folders, &folder->entry );
1511 return ERROR_SUCCESS;
1514 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1516 FolderList *fl;
1518 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1519 fl->folder = child;
1520 list_add_tail( &parent->children, &fl->entry );
1521 return ERROR_SUCCESS;
1524 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1526 MSIPACKAGE *package = param;
1527 MSIFOLDER *parent, *child;
1529 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1530 return ERROR_FUNCTION_FAILED;
1532 if (!child->Parent) return ERROR_SUCCESS;
1534 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1535 return ERROR_FUNCTION_FAILED;
1537 return add_folder_child( parent, child );
1540 static UINT load_all_folders( MSIPACKAGE *package )
1542 static const WCHAR query[] = {
1543 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1544 '`','D','i','r','e','c','t','o','r','y','`',0};
1545 MSIQUERY *view;
1546 UINT r;
1548 if (!list_empty(&package->folders))
1549 return ERROR_SUCCESS;
1551 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1552 if (r != ERROR_SUCCESS)
1553 return r;
1555 r = MSI_IterateRecords( view, NULL, load_folder, package );
1556 if (r != ERROR_SUCCESS)
1558 msiobj_release( &view->hdr );
1559 return r;
1561 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1562 msiobj_release( &view->hdr );
1563 return r;
1566 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1568 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1569 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1571 load_all_folders( package );
1572 msi_load_all_components( package );
1573 msi_load_all_features( package );
1574 load_all_files( package );
1575 load_all_patches( package );
1576 load_all_media( package );
1578 return ERROR_SUCCESS;
1581 static UINT execute_script( MSIPACKAGE *package, UINT script )
1583 UINT i, rc = ERROR_SUCCESS;
1585 TRACE("executing script %u\n", script);
1587 package->script = script;
1589 if (script == SCRIPT_ROLLBACK)
1591 for (i = package->script_actions_count[script]; i > 0; i--)
1593 rc = ACTION_PerformAction(package, package->script_actions[script][i-1]);
1594 if (rc != ERROR_SUCCESS)
1596 ERR("Execution of script %i halted; action %s returned %u\n",
1597 script, debugstr_w(package->script_actions[script][i-1]), rc);
1598 break;
1602 else
1604 for (i = 0; i < package->script_actions_count[script]; i++)
1606 rc = ACTION_PerformAction(package, package->script_actions[script][i]);
1607 if (rc != ERROR_SUCCESS)
1609 ERR("Execution of script %i halted; action %s returned %u\n",
1610 script, debugstr_w(package->script_actions[script][i]), rc);
1611 break;
1616 package->script = SCRIPT_NONE;
1618 msi_free_action_script(package, script);
1619 return rc;
1622 static UINT ACTION_FileCost(MSIPACKAGE *package)
1624 return ERROR_SUCCESS;
1627 static void get_client_counts( MSIPACKAGE *package )
1629 MSICOMPONENT *comp;
1630 HKEY hkey;
1632 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1634 if (!comp->ComponentId) continue;
1636 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1637 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1639 comp->num_clients = 0;
1640 continue;
1642 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1643 NULL, NULL, NULL, NULL );
1644 RegCloseKey( hkey );
1648 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1650 MSICOMPONENT *comp;
1651 UINT r;
1653 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1655 if (!comp->ComponentId) continue;
1657 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1658 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1659 &comp->Installed );
1660 if (r == ERROR_SUCCESS) continue;
1662 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1663 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1664 &comp->Installed );
1665 if (r == ERROR_SUCCESS) continue;
1667 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1668 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1669 &comp->Installed );
1670 if (r == ERROR_SUCCESS) continue;
1672 comp->Installed = INSTALLSTATE_ABSENT;
1676 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1678 MSIFEATURE *feature;
1680 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1682 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1684 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1685 feature->Installed = INSTALLSTATE_ABSENT;
1686 else
1687 feature->Installed = state;
1691 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1693 return (feature->Level > 0 && feature->Level <= level);
1696 static BOOL process_state_property(MSIPACKAGE* package, int level,
1697 LPCWSTR property, INSTALLSTATE state)
1699 LPWSTR override;
1700 MSIFEATURE *feature;
1701 BOOL remove = !strcmpW(property, szRemove);
1702 BOOL reinstall = !strcmpW(property, szReinstall);
1704 override = msi_dup_property( package->db, property );
1705 if (!override)
1706 return FALSE;
1708 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1710 if (feature->Level <= 0)
1711 continue;
1713 if (reinstall)
1714 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : feature->Installed);
1715 else if (remove)
1716 state = (feature->Installed == INSTALLSTATE_ABSENT ? INSTALLSTATE_UNKNOWN : INSTALLSTATE_ABSENT);
1718 if (!strcmpiW( override, szAll ))
1720 feature->Action = state;
1721 feature->ActionRequest = state;
1723 else
1725 LPWSTR ptr = override;
1726 LPWSTR ptr2 = strchrW(override,',');
1728 while (ptr)
1730 int len = ptr2 - ptr;
1732 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1733 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1735 feature->Action = state;
1736 feature->ActionRequest = state;
1737 break;
1739 if (ptr2)
1741 ptr=ptr2+1;
1742 ptr2 = strchrW(ptr,',');
1744 else
1745 break;
1749 msi_free(override);
1750 return TRUE;
1753 static BOOL process_overrides( MSIPACKAGE *package, int level )
1755 static const WCHAR szAddLocal[] =
1756 {'A','D','D','L','O','C','A','L',0};
1757 static const WCHAR szAddSource[] =
1758 {'A','D','D','S','O','U','R','C','E',0};
1759 static const WCHAR szAdvertise[] =
1760 {'A','D','V','E','R','T','I','S','E',0};
1761 BOOL ret = FALSE;
1763 /* all these activation/deactivation things happen in order and things
1764 * later on the list override things earlier on the list.
1766 * 0 INSTALLLEVEL processing
1767 * 1 ADDLOCAL
1768 * 2 REMOVE
1769 * 3 ADDSOURCE
1770 * 4 ADDDEFAULT
1771 * 5 REINSTALL
1772 * 6 ADVERTISE
1773 * 7 COMPADDLOCAL
1774 * 8 COMPADDSOURCE
1775 * 9 FILEADDLOCAL
1776 * 10 FILEADDSOURCE
1777 * 11 FILEADDDEFAULT
1779 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1780 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1781 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1782 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1783 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1785 if (ret)
1786 msi_set_property( package->db, szPreselected, szOne, -1 );
1788 return ret;
1791 static void disable_children( MSIFEATURE *feature, int level )
1793 FeatureList *fl;
1795 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1797 if (!is_feature_selected( feature, level ))
1799 TRACE("child %s (level %d request %d) follows disabled parent %s (level %d request %d)\n",
1800 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1801 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1803 fl->feature->Level = feature->Level;
1804 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1805 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1807 disable_children( fl->feature, level );
1811 static void follow_parent( MSIFEATURE *feature )
1813 FeatureList *fl;
1815 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1817 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1819 TRACE("child %s (level %d request %d) follows parent %s (level %d request %d)\n",
1820 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1821 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1823 fl->feature->Action = feature->Action;
1824 fl->feature->ActionRequest = feature->ActionRequest;
1826 follow_parent( fl->feature );
1830 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1832 int level;
1833 MSICOMPONENT* component;
1834 MSIFEATURE *feature;
1836 TRACE("Checking Install Level\n");
1838 level = msi_get_property_int(package->db, szInstallLevel, 1);
1840 if (msi_get_property_int( package->db, szPreselected, 0 ))
1842 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 if (!is_feature_selected( feature, level )) continue;
1846 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1848 if (feature->Installed == INSTALLSTATE_ABSENT)
1850 feature->Action = INSTALLSTATE_UNKNOWN;
1851 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1853 else
1855 feature->Action = feature->Installed;
1856 feature->ActionRequest = feature->Installed;
1860 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1862 if (feature->Feature_Parent) continue;
1863 disable_children( feature, level );
1864 follow_parent( feature );
1867 else if (!msi_get_property_int( package->db, szInstalled, 0 ))
1869 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1871 if (!is_feature_selected( feature, level )) continue;
1873 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1875 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1877 feature->Action = INSTALLSTATE_SOURCE;
1878 feature->ActionRequest = INSTALLSTATE_SOURCE;
1880 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1882 feature->Action = INSTALLSTATE_ADVERTISED;
1883 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1885 else
1887 feature->Action = INSTALLSTATE_LOCAL;
1888 feature->ActionRequest = INSTALLSTATE_LOCAL;
1892 /* disable child features of unselected parent or follow parent */
1893 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1895 if (feature->Feature_Parent) continue;
1896 disable_children( feature, level );
1897 follow_parent( feature );
1901 /* now we want to set component state based based on feature state */
1902 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1904 ComponentList *cl;
1906 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1907 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1908 feature->ActionRequest, feature->Action);
1910 /* features with components that have compressed files are made local */
1911 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1913 if (cl->component->ForceLocalState &&
1914 feature->ActionRequest == INSTALLSTATE_SOURCE)
1916 feature->Action = INSTALLSTATE_LOCAL;
1917 feature->ActionRequest = INSTALLSTATE_LOCAL;
1918 break;
1922 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1924 component = cl->component;
1926 switch (feature->ActionRequest)
1928 case INSTALLSTATE_ABSENT:
1929 component->anyAbsent = 1;
1930 break;
1931 case INSTALLSTATE_ADVERTISED:
1932 component->hasAdvertisedFeature = 1;
1933 break;
1934 case INSTALLSTATE_SOURCE:
1935 component->hasSourceFeature = 1;
1936 break;
1937 case INSTALLSTATE_LOCAL:
1938 component->hasLocalFeature = 1;
1939 break;
1940 case INSTALLSTATE_DEFAULT:
1941 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1942 component->hasAdvertisedFeature = 1;
1943 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1944 component->hasSourceFeature = 1;
1945 else
1946 component->hasLocalFeature = 1;
1947 break;
1948 case INSTALLSTATE_UNKNOWN:
1949 if (feature->Installed == INSTALLSTATE_ADVERTISED)
1950 component->hasAdvertisedFeature = 1;
1951 if (feature->Installed == INSTALLSTATE_SOURCE)
1952 component->hasSourceFeature = 1;
1953 if (feature->Installed == INSTALLSTATE_LOCAL)
1954 component->hasLocalFeature = 1;
1955 break;
1956 default:
1957 break;
1962 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1964 /* check if it's local or source */
1965 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1966 (component->hasLocalFeature || component->hasSourceFeature))
1968 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1969 !component->ForceLocalState)
1971 component->Action = INSTALLSTATE_SOURCE;
1972 component->ActionRequest = INSTALLSTATE_SOURCE;
1974 else
1976 component->Action = INSTALLSTATE_LOCAL;
1977 component->ActionRequest = INSTALLSTATE_LOCAL;
1979 continue;
1982 /* if any feature is local, the component must be local too */
1983 if (component->hasLocalFeature)
1985 component->Action = INSTALLSTATE_LOCAL;
1986 component->ActionRequest = INSTALLSTATE_LOCAL;
1987 continue;
1989 if (component->hasSourceFeature)
1991 component->Action = INSTALLSTATE_SOURCE;
1992 component->ActionRequest = INSTALLSTATE_SOURCE;
1993 continue;
1995 if (component->hasAdvertisedFeature)
1997 component->Action = INSTALLSTATE_ADVERTISED;
1998 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1999 continue;
2001 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2002 if (component->anyAbsent && component->ComponentId)
2004 component->Action = INSTALLSTATE_ABSENT;
2005 component->ActionRequest = INSTALLSTATE_ABSENT;
2009 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2011 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
2013 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2014 component->Action = INSTALLSTATE_LOCAL;
2015 component->ActionRequest = INSTALLSTATE_LOCAL;
2018 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
2019 component->Installed == INSTALLSTATE_SOURCE &&
2020 component->hasSourceFeature)
2022 component->Action = INSTALLSTATE_UNKNOWN;
2023 component->ActionRequest = INSTALLSTATE_UNKNOWN;
2026 TRACE("component %s (installed %d request %d action %d)\n",
2027 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2029 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
2030 component->num_clients++;
2031 else if (component->Action == INSTALLSTATE_ABSENT)
2032 component->num_clients--;
2035 return ERROR_SUCCESS;
2038 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2040 MSIPACKAGE *package = param;
2041 LPCWSTR name;
2042 MSIFEATURE *feature;
2044 name = MSI_RecordGetString( row, 1 );
2046 feature = msi_get_loaded_feature( package, name );
2047 if (!feature)
2048 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2049 else
2051 LPCWSTR Condition;
2052 Condition = MSI_RecordGetString(row,3);
2054 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2056 int level = MSI_RecordGetInteger(row,2);
2057 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2058 feature->Level = level;
2061 return ERROR_SUCCESS;
2064 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2066 static const WCHAR name[] = {'\\',0};
2067 VS_FIXEDFILEINFO *ptr, *ret;
2068 LPVOID version;
2069 DWORD versize, handle;
2070 UINT sz;
2072 versize = GetFileVersionInfoSizeW( filename, &handle );
2073 if (!versize)
2074 return NULL;
2076 version = msi_alloc( versize );
2077 if (!version)
2078 return NULL;
2080 GetFileVersionInfoW( filename, 0, versize, version );
2082 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2084 msi_free( version );
2085 return NULL;
2088 ret = msi_alloc( sz );
2089 memcpy( ret, ptr, sz );
2091 msi_free( version );
2092 return ret;
2095 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2097 DWORD ms, ls;
2099 msi_parse_version_string( version, &ms, &ls );
2101 if (fi->dwFileVersionMS > ms) return 1;
2102 else if (fi->dwFileVersionMS < ms) return -1;
2103 else if (fi->dwFileVersionLS > ls) return 1;
2104 else if (fi->dwFileVersionLS < ls) return -1;
2105 return 0;
2108 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2110 DWORD ms1, ms2;
2112 msi_parse_version_string( ver1, &ms1, NULL );
2113 msi_parse_version_string( ver2, &ms2, NULL );
2115 if (ms1 > ms2) return 1;
2116 else if (ms1 < ms2) return -1;
2117 return 0;
2120 DWORD msi_get_disk_file_size( LPCWSTR filename )
2122 HANDLE file;
2123 DWORD size;
2125 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2126 if (file == INVALID_HANDLE_VALUE)
2127 return INVALID_FILE_SIZE;
2129 size = GetFileSize( file, NULL );
2130 CloseHandle( file );
2131 return size;
2134 BOOL msi_file_hash_matches( MSIFILE *file )
2136 UINT r;
2137 MSIFILEHASHINFO hash;
2139 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2140 r = msi_get_filehash( file->TargetPath, &hash );
2141 if (r != ERROR_SUCCESS)
2142 return FALSE;
2144 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2147 static WCHAR *create_temp_dir( MSIDATABASE *db )
2149 static UINT id;
2150 WCHAR *ret;
2152 if (!db->tempfolder)
2154 WCHAR tmp[MAX_PATH];
2155 UINT len = ARRAY_SIZE( tmp );
2157 if (msi_get_property( db, szTempFolder, tmp, &len ) ||
2158 GetFileAttributesW( tmp ) != FILE_ATTRIBUTE_DIRECTORY)
2160 GetTempPathW( MAX_PATH, tmp );
2162 if (!(db->tempfolder = strdupW( tmp ))) return NULL;
2165 if ((ret = msi_alloc( (strlenW( db->tempfolder ) + 20) * sizeof(WCHAR) )))
2167 for (;;)
2169 if (!GetTempFileNameW( db->tempfolder, szMsi, ++id, ret ))
2171 msi_free( ret );
2172 return NULL;
2174 if (CreateDirectoryW( ret, NULL )) break;
2178 return ret;
2182 * msi_build_directory_name()
2184 * This function is to save messing round with directory names
2185 * It handles adding backslashes between path segments,
2186 * and can add \ at the end of the directory name if told to.
2188 * It takes a variable number of arguments.
2189 * It always allocates a new string for the result, so make sure
2190 * to free the return value when finished with it.
2192 * The first arg is the number of path segments that follow.
2193 * The arguments following count are a list of path segments.
2194 * A path segment may be NULL.
2196 * Path segments will be added with a \ separating them.
2197 * A \ will not be added after the last segment, however if the
2198 * last segment is NULL, then the last character will be a \
2200 WCHAR *msi_build_directory_name( DWORD count, ... )
2202 DWORD sz = 1, i;
2203 WCHAR *dir;
2204 va_list va;
2206 va_start( va, count );
2207 for (i = 0; i < count; i++)
2209 const WCHAR *str = va_arg( va, const WCHAR * );
2210 if (str) sz += strlenW( str ) + 1;
2212 va_end( va );
2214 dir = msi_alloc( sz * sizeof(WCHAR) );
2215 dir[0] = 0;
2217 va_start( va, count );
2218 for (i = 0; i < count; i++)
2220 const WCHAR *str = va_arg( va, const WCHAR * );
2221 if (!str) continue;
2222 strcatW( dir, str );
2223 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2225 va_end( va );
2226 return dir;
2229 BOOL msi_is_global_assembly( MSICOMPONENT *comp )
2231 return comp->assembly && !comp->assembly->application;
2234 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2236 msi_free( file->TargetPath );
2237 if (msi_is_global_assembly( file->Component ))
2239 MSIASSEMBLY *assembly = file->Component->assembly;
2241 if (!assembly->tempdir) assembly->tempdir = create_temp_dir( package->db );
2242 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2244 else
2246 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2247 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2250 TRACE("file %s resolves to %s\n", debugstr_w(file->File), debugstr_w(file->TargetPath));
2253 static UINT calculate_file_cost( MSIPACKAGE *package )
2255 VS_FIXEDFILEINFO *file_version;
2256 WCHAR *font_version;
2257 MSIFILE *file;
2259 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2261 MSICOMPONENT *comp = file->Component;
2262 DWORD file_size;
2264 if (!comp->Enabled) continue;
2266 if (file->IsCompressed)
2267 comp->ForceLocalState = TRUE;
2269 set_target_path( package, file );
2271 if ((comp->assembly && !comp->assembly->installed) ||
2272 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2274 comp->Cost += file->FileSize;
2275 continue;
2277 file_size = msi_get_disk_file_size( file->TargetPath );
2278 TRACE("%s (size %u)\n", debugstr_w(file->TargetPath), file_size);
2280 if (file->Version)
2282 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2284 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2286 comp->Cost += file->FileSize - file_size;
2288 msi_free( file_version );
2289 continue;
2291 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2293 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2295 comp->Cost += file->FileSize - file_size;
2297 msi_free( font_version );
2298 continue;
2301 if (file_size != file->FileSize)
2303 comp->Cost += file->FileSize - file_size;
2306 return ERROR_SUCCESS;
2309 WCHAR *msi_normalize_path( const WCHAR *in )
2311 const WCHAR *p = in;
2312 WCHAR *q, *ret;
2313 int n, len = strlenW( in ) + 2;
2315 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2317 len = 0;
2318 while (1)
2320 /* copy until the end of the string or a space */
2321 while (*p != ' ' && (*q = *p))
2323 p++, len++;
2324 /* reduce many backslashes to one */
2325 if (*p != '\\' || *q != '\\')
2326 q++;
2329 /* quit at the end of the string */
2330 if (!*p)
2331 break;
2333 /* count the number of spaces */
2334 n = 0;
2335 while (p[n] == ' ')
2336 n++;
2338 /* if it's leading or trailing space, skip it */
2339 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2340 p += n;
2341 else /* copy n spaces */
2342 while (n && (*q++ = *p++)) n--;
2344 while (q - ret > 0 && q[-1] == ' ') q--;
2345 if (q - ret > 0 && q[-1] != '\\')
2347 q[0] = '\\';
2348 q[1] = 0;
2350 return ret;
2353 static WCHAR *get_install_location( MSIPACKAGE *package )
2355 HKEY hkey;
2356 WCHAR *path;
2358 if (!package->ProductCode) return NULL;
2359 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2360 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2362 msi_free( path );
2363 path = NULL;
2365 RegCloseKey( hkey );
2366 return path;
2369 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2371 FolderList *fl;
2372 MSIFOLDER *folder, *parent, *child;
2373 WCHAR *path, *normalized_path;
2375 TRACE("resolving %s\n", debugstr_w(name));
2377 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2379 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2381 if (!(path = get_install_location( package )) &&
2382 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2384 path = msi_dup_property( package->db, szRootDrive );
2387 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2389 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2391 parent = msi_get_loaded_folder( package, folder->Parent );
2392 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2394 else
2395 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2398 normalized_path = msi_normalize_path( path );
2399 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2400 msi_free( path );
2402 msi_free( folder->ResolvedTarget );
2403 folder->ResolvedTarget = normalized_path;
2405 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2407 child = fl->folder;
2408 msi_resolve_target_folder( package, child->Directory, load_prop );
2410 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2413 static ULONGLONG get_volume_space_required( MSIPACKAGE *package )
2415 MSICOMPONENT *comp;
2416 ULONGLONG ret = 0;
2418 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2420 if (comp->Action == INSTALLSTATE_LOCAL) ret += comp->Cost;
2422 return ret;
2425 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2427 static const WCHAR query[] =
2428 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2429 '`','C','o','n','d','i','t','i','o','n','`',0};
2430 static const WCHAR szOutOfDiskSpace[] =
2431 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2432 static const WCHAR szPrimaryFolder[] =
2433 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2434 static const WCHAR szPrimaryVolumePath[] =
2435 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2436 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2437 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2438 'A','v','a','i','l','a','b','l','e',0};
2439 static const WCHAR szPrimaryVolumeSpaceRequired[] =
2440 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2441 'R','e','q','u','i','r','e','d',0};
2442 static const WCHAR szPrimaryVolumeSpaceRemaining[] =
2443 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2444 'R','e','m','a','i','n','i','n','g',0};
2445 static const WCHAR szOutOfNoRbDiskSpace[] =
2446 {'O','u','t','O','f','N','o','R','b','D','i','s','k','S','p','a','c','e',0};
2447 MSICOMPONENT *comp;
2448 MSIQUERY *view;
2449 WCHAR *level, *primary_key, *primary_folder;
2450 UINT rc;
2452 TRACE("Building directory properties\n");
2453 msi_resolve_target_folder( package, szTargetDir, TRUE );
2455 TRACE("Evaluating component conditions\n");
2456 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2458 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2460 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2461 comp->Enabled = FALSE;
2463 else
2464 comp->Enabled = TRUE;
2466 get_client_counts( package );
2468 /* read components states from the registry */
2469 ACTION_GetComponentInstallStates(package);
2470 ACTION_GetFeatureInstallStates(package);
2472 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2474 TRACE("Evaluating feature conditions\n");
2476 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2477 if (rc == ERROR_SUCCESS)
2479 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2480 msiobj_release( &view->hdr );
2481 if (rc != ERROR_SUCCESS)
2482 return rc;
2486 TRACE("Calculating file cost\n");
2487 calculate_file_cost( package );
2489 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2490 /* set default run level if not set */
2491 level = msi_dup_property( package->db, szInstallLevel );
2492 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2493 msi_free(level);
2495 if ((rc = MSI_SetFeatureStates( package ))) return rc;
2497 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2499 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2501 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2502 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2504 static const WCHAR fmtW[] = {'%','l','u',0};
2505 ULARGE_INTEGER free;
2506 ULONGLONG required;
2507 WCHAR buf[21];
2509 primary_folder[2] = 0;
2510 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2512 sprintfW( buf, fmtW, free.QuadPart / 512 );
2513 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2515 required = get_volume_space_required( package );
2516 sprintfW( buf, fmtW, required / 512 );
2517 msi_set_property( package->db, szPrimaryVolumeSpaceRequired, buf, -1 );
2519 sprintfW( buf, fmtW, (free.QuadPart - required) / 512 );
2520 msi_set_property( package->db, szPrimaryVolumeSpaceRemaining, buf, -1 );
2521 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2523 msi_free( primary_folder );
2525 msi_free( primary_key );
2528 /* FIXME: check volume disk space */
2529 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2530 msi_set_property( package->db, szOutOfNoRbDiskSpace, szZero, -1 );
2532 return ERROR_SUCCESS;
2535 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD len, DWORD *type, DWORD *size )
2537 BYTE *data;
2539 if (!value)
2541 *size = sizeof(WCHAR);
2542 *type = REG_SZ;
2543 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2544 return data;
2546 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2548 if (value[1]=='x')
2550 LPWSTR ptr;
2551 CHAR byte[5];
2552 LPWSTR deformated = NULL;
2553 int count;
2555 deformat_string(package, &value[2], &deformated);
2557 /* binary value type */
2558 ptr = deformated;
2559 *type = REG_BINARY;
2560 if (strlenW(ptr)%2)
2561 *size = (strlenW(ptr)/2)+1;
2562 else
2563 *size = strlenW(ptr)/2;
2565 data = msi_alloc(*size);
2567 byte[0] = '0';
2568 byte[1] = 'x';
2569 byte[4] = 0;
2570 count = 0;
2571 /* if uneven pad with a zero in front */
2572 if (strlenW(ptr)%2)
2574 byte[2]= '0';
2575 byte[3]= *ptr;
2576 ptr++;
2577 data[count] = (BYTE)strtol(byte,NULL,0);
2578 count ++;
2579 TRACE("Uneven byte count\n");
2581 while (*ptr)
2583 byte[2]= *ptr;
2584 ptr++;
2585 byte[3]= *ptr;
2586 ptr++;
2587 data[count] = (BYTE)strtol(byte,NULL,0);
2588 count ++;
2590 msi_free(deformated);
2592 TRACE("Data %i bytes(%i)\n",*size,count);
2594 else
2596 LPWSTR deformated;
2597 LPWSTR p;
2598 DWORD d = 0;
2599 deformat_string(package, &value[1], &deformated);
2601 *type=REG_DWORD;
2602 *size = sizeof(DWORD);
2603 data = msi_alloc(*size);
2604 p = deformated;
2605 if (*p == '-')
2606 p++;
2607 while (*p)
2609 if ( (*p < '0') || (*p > '9') )
2610 break;
2611 d *= 10;
2612 d += (*p - '0');
2613 p++;
2615 if (deformated[0] == '-')
2616 d = -d;
2617 *(LPDWORD)data = d;
2618 TRACE("DWORD %i\n",*(LPDWORD)data);
2620 msi_free(deformated);
2623 else
2625 const WCHAR *ptr = value;
2627 *type = REG_SZ;
2628 if (value[0] == '#')
2630 ptr++; len--;
2631 if (value[1] == '%')
2633 ptr++; len--;
2634 *type = REG_EXPAND_SZ;
2637 data = (BYTE *)msi_strdupW( ptr, len );
2638 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2639 *size = (len + 1) * sizeof(WCHAR);
2641 return data;
2644 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2646 const WCHAR *ret;
2648 switch (root)
2650 case -1:
2651 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2653 *root_key = HKEY_LOCAL_MACHINE;
2654 ret = szHLM;
2656 else
2658 *root_key = HKEY_CURRENT_USER;
2659 ret = szHCU;
2661 break;
2662 case 0:
2663 *root_key = HKEY_CLASSES_ROOT;
2664 ret = szHCR;
2665 break;
2666 case 1:
2667 *root_key = HKEY_CURRENT_USER;
2668 ret = szHCU;
2669 break;
2670 case 2:
2671 *root_key = HKEY_LOCAL_MACHINE;
2672 ret = szHLM;
2673 break;
2674 case 3:
2675 *root_key = HKEY_USERS;
2676 ret = szHU;
2677 break;
2678 default:
2679 ERR("Unknown root %i\n", root);
2680 return NULL;
2683 return ret;
2686 static inline REGSAM get_registry_view( const MSICOMPONENT *comp )
2688 REGSAM view = 0;
2689 if (is_wow64 || is_64bit)
2690 view |= (comp->Attributes & msidbComponentAttributes64bit) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
2691 return view;
2694 static HKEY open_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, BOOL create, REGSAM access )
2696 WCHAR *subkey, *p, *q;
2697 HKEY hkey, ret = NULL;
2698 LONG res;
2700 access |= get_registry_view( comp );
2702 if (!(subkey = strdupW( path ))) return NULL;
2703 p = subkey;
2704 if ((q = strchrW( p, '\\' ))) *q = 0;
2705 if (create)
2706 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2707 else
2708 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2709 if (res)
2711 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2712 msi_free( subkey );
2713 return NULL;
2715 if (q && q[1])
2717 ret = open_key( comp, hkey, q + 1, create, access );
2718 RegCloseKey( hkey );
2720 else ret = hkey;
2721 msi_free( subkey );
2722 return ret;
2725 static BOOL is_special_entry( const WCHAR *name )
2727 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2730 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2732 const WCHAR *p = str;
2733 WCHAR **ret;
2734 int i = 0;
2736 *count = 0;
2737 if (!str) return NULL;
2738 while ((p - str) < len)
2740 p += strlenW( p ) + 1;
2741 (*count)++;
2743 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2744 p = str;
2745 while ((p - str) < len)
2747 if (!(ret[i] = strdupW( p )))
2749 for (; i >= 0; i--) msi_free( ret[i] );
2750 msi_free( ret );
2751 return NULL;
2753 p += strlenW( p ) + 1;
2754 i++;
2756 return ret;
2759 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2760 WCHAR **right, DWORD right_count, DWORD *size )
2762 WCHAR *ret, *p;
2763 unsigned int i;
2765 *size = sizeof(WCHAR);
2766 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2767 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2769 if (!(ret = p = msi_alloc( *size ))) return NULL;
2771 for (i = 0; i < left_count; i++)
2773 strcpyW( p, left[i] );
2774 p += strlenW( p ) + 1;
2776 for (i = 0; i < right_count; i++)
2778 strcpyW( p, right[i] );
2779 p += strlenW( p ) + 1;
2781 *p = 0;
2782 return ret;
2785 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2786 WCHAR **new, DWORD new_count )
2788 DWORD ret = old_count;
2789 unsigned int i, j, k;
2791 for (i = 0; i < new_count; i++)
2793 for (j = 0; j < old_count; j++)
2795 if (old[j] && !strcmpW( new[i], old[j] ))
2797 msi_free( old[j] );
2798 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2799 old[k] = NULL;
2800 ret--;
2804 return ret;
2807 enum join_op
2809 JOIN_OP_APPEND,
2810 JOIN_OP_PREPEND,
2811 JOIN_OP_REPLACE
2814 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2815 WCHAR **new, DWORD new_count, DWORD *size )
2817 switch (op)
2819 case JOIN_OP_APPEND:
2820 old_count = remove_duplicate_values( old, old_count, new, new_count );
2821 return flatten_multi_string_values( old, old_count, new, new_count, size );
2823 case JOIN_OP_PREPEND:
2824 old_count = remove_duplicate_values( old, old_count, new, new_count );
2825 return flatten_multi_string_values( new, new_count, old, old_count, size );
2827 case JOIN_OP_REPLACE:
2828 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2830 default:
2831 ERR("unhandled join op %u\n", op);
2832 return NULL;
2836 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2837 BYTE *new_value, DWORD new_size, DWORD *size )
2839 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2840 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2841 enum join_op op = JOIN_OP_REPLACE;
2842 WCHAR **old = NULL, **new = NULL;
2843 BYTE *ret;
2845 if (new_size / sizeof(WCHAR) - 1 > 1)
2847 new_ptr = (const WCHAR *)new_value;
2848 new_len = new_size / sizeof(WCHAR) - 1;
2850 if (!new_ptr[0] && new_ptr[new_len - 1])
2852 op = JOIN_OP_APPEND;
2853 new_len--;
2854 new_ptr++;
2856 else if (new_ptr[0] && !new_ptr[new_len - 1])
2858 op = JOIN_OP_PREPEND;
2859 new_len--;
2861 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2863 op = JOIN_OP_REPLACE;
2864 new_len -= 2;
2865 new_ptr++;
2867 new = split_multi_string_values( new_ptr, new_len, &new_count );
2869 if (old_size / sizeof(WCHAR) - 1 > 1)
2871 old_ptr = (const WCHAR *)old_value;
2872 old_len = old_size / sizeof(WCHAR) - 1;
2873 old = split_multi_string_values( old_ptr, old_len, &old_count );
2875 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2876 for (i = 0; i < old_count; i++) msi_free( old[i] );
2877 for (i = 0; i < new_count; i++) msi_free( new[i] );
2878 msi_free( old );
2879 msi_free( new );
2880 return ret;
2883 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2885 BYTE *ret;
2886 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2887 if (!(ret = msi_alloc( *size ))) return NULL;
2888 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2889 return ret;
2892 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2894 MSIPACKAGE *package = param;
2895 BYTE *new_value, *old_value = NULL;
2896 HKEY root_key, hkey;
2897 DWORD type, old_type, new_size, old_size = 0;
2898 LPWSTR deformated, uikey;
2899 const WCHAR *szRoot, *component, *name, *key, *str;
2900 MSICOMPONENT *comp;
2901 MSIRECORD * uirow;
2902 INT root;
2903 BOOL check_first = FALSE;
2904 int len;
2906 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2908 component = MSI_RecordGetString(row, 6);
2909 comp = msi_get_loaded_component(package,component);
2910 if (!comp)
2911 return ERROR_SUCCESS;
2913 comp->Action = msi_get_component_action( package, comp );
2914 if (comp->Action != INSTALLSTATE_LOCAL && comp->Action != INSTALLSTATE_SOURCE)
2916 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2917 return ERROR_SUCCESS;
2920 name = MSI_RecordGetString(row, 4);
2921 if( MSI_RecordIsNull(row,5) && name )
2923 /* null values can have special meanings */
2924 if (name[0]=='-' && name[1] == 0)
2925 return ERROR_SUCCESS;
2926 if ((name[0] == '+' || name[0] == '*') && !name[1])
2927 check_first = TRUE;
2930 root = MSI_RecordGetInteger(row,2);
2931 key = MSI_RecordGetString(row, 3);
2933 szRoot = get_root_key( package, root, &root_key );
2934 if (!szRoot)
2935 return ERROR_SUCCESS;
2937 deformat_string(package, key , &deformated);
2938 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2939 strcpyW(uikey,szRoot);
2940 strcatW(uikey,deformated);
2942 if (!(hkey = open_key( comp, root_key, deformated, TRUE, KEY_QUERY_VALUE | KEY_SET_VALUE )))
2944 ERR("Could not create key %s\n", debugstr_w(deformated));
2945 msi_free(uikey);
2946 msi_free(deformated);
2947 return ERROR_FUNCTION_FAILED;
2949 msi_free( deformated );
2950 str = msi_record_get_string( row, 5, NULL );
2951 len = deformat_string( package, str, &deformated );
2952 new_value = parse_value( package, deformated, len, &type, &new_size );
2954 msi_free( deformated );
2955 deformat_string(package, name, &deformated);
2957 if (!is_special_entry( name ))
2959 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2960 if (type == REG_MULTI_SZ)
2962 BYTE *new;
2963 if (old_value && old_type != REG_MULTI_SZ)
2965 msi_free( old_value );
2966 old_value = NULL;
2967 old_size = 0;
2969 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2970 msi_free( new_value );
2971 new_value = new;
2973 if (!check_first)
2975 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2976 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2978 else if (!old_value)
2980 if (deformated || new_size)
2982 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2983 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2986 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2988 RegCloseKey(hkey);
2990 uirow = MSI_CreateRecord(3);
2991 MSI_RecordSetStringW(uirow,2,deformated);
2992 MSI_RecordSetStringW(uirow,1,uikey);
2993 if (type == REG_SZ || type == REG_EXPAND_SZ)
2994 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2995 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
2996 msiobj_release( &uirow->hdr );
2998 msi_free(new_value);
2999 msi_free(old_value);
3000 msi_free(deformated);
3001 msi_free(uikey);
3003 return ERROR_SUCCESS;
3006 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
3008 static const WCHAR query[] = {
3009 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3010 '`','R','e','g','i','s','t','r','y','`',0};
3011 MSIQUERY *view;
3012 UINT rc;
3014 if (package->script == SCRIPT_NONE)
3015 return msi_schedule_action(package, SCRIPT_INSTALL, szWriteRegistryValues);
3017 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3018 if (rc != ERROR_SUCCESS)
3019 return ERROR_SUCCESS;
3021 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
3022 msiobj_release(&view->hdr);
3023 return rc;
3026 static int is_key_empty(const MSICOMPONENT *comp, HKEY root, const WCHAR *path)
3028 DWORD subkeys, values;
3029 HKEY key;
3030 LONG res;
3032 key = open_key(comp, root, path, FALSE, get_registry_view(comp) | KEY_READ);
3033 if (!key) return 0;
3035 res = RegQueryInfoKeyW(key, 0, 0, 0, &subkeys, 0, 0, &values, 0, 0, 0, 0);
3036 RegCloseKey(key);
3038 return !res && !subkeys && !values;
3041 static void delete_key( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3043 LONG res = ERROR_SUCCESS;
3044 REGSAM access = 0;
3045 WCHAR *subkey, *p;
3046 HKEY hkey;
3048 access |= get_registry_view( comp );
3050 if (!(subkey = strdupW( path ))) return;
3053 if ((p = strrchrW( subkey, '\\' )))
3055 *p = 0;
3056 if (!p[1]) continue; /* trailing backslash */
3057 hkey = open_key( comp, root, subkey, FALSE, access | READ_CONTROL );
3058 if (!hkey) break;
3059 if (!is_key_empty(comp, hkey, p + 1))
3061 RegCloseKey(hkey);
3062 break;
3064 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
3065 RegCloseKey( hkey );
3067 else if (is_key_empty(comp, root, subkey))
3068 res = RegDeleteKeyExW( root, subkey, access, 0 );
3069 if (res)
3071 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
3072 break;
3074 } while (p);
3075 msi_free( subkey );
3078 static void delete_value( const MSICOMPONENT *comp, HKEY root, const WCHAR *path, const WCHAR *value )
3080 LONG res;
3081 HKEY hkey;
3083 if ((hkey = open_key( comp, root, path, FALSE, KEY_SET_VALUE | KEY_QUERY_VALUE )))
3085 if ((res = RegDeleteValueW( hkey, value )))
3086 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3088 RegCloseKey( hkey );
3089 if (is_key_empty(comp, root, path))
3091 TRACE("removing empty key %s\n", debugstr_w(path));
3092 delete_key( comp, root, path );
3097 static void delete_tree( const MSICOMPONENT *comp, HKEY root, const WCHAR *path )
3099 LONG res;
3100 HKEY hkey;
3102 if (!(hkey = open_key( comp, root, path, FALSE, KEY_ALL_ACCESS ))) return;
3103 res = RegDeleteTreeW( hkey, NULL );
3104 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3105 delete_key( comp, root, path );
3106 RegCloseKey( hkey );
3109 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3111 MSIPACKAGE *package = param;
3112 LPCWSTR component, name, key_str, root_key_str;
3113 LPWSTR deformated_key, deformated_name, ui_key_str;
3114 MSICOMPONENT *comp;
3115 MSIRECORD *uirow;
3116 BOOL delete_key = FALSE;
3117 HKEY hkey_root;
3118 UINT size;
3119 INT root;
3121 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3123 component = MSI_RecordGetString( row, 6 );
3124 comp = msi_get_loaded_component( package, component );
3125 if (!comp)
3126 return ERROR_SUCCESS;
3128 comp->Action = msi_get_component_action( package, comp );
3129 if (comp->Action != INSTALLSTATE_ABSENT)
3131 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3132 return ERROR_SUCCESS;
3135 name = MSI_RecordGetString( row, 4 );
3136 if (MSI_RecordIsNull( row, 5 ) && name )
3138 if (name[0] == '+' && !name[1])
3139 return ERROR_SUCCESS;
3140 if ((name[0] == '-' || name[0] == '*') && !name[1])
3142 delete_key = TRUE;
3143 name = NULL;
3147 root = MSI_RecordGetInteger( row, 2 );
3148 key_str = MSI_RecordGetString( row, 3 );
3150 root_key_str = get_root_key( package, root, &hkey_root );
3151 if (!root_key_str)
3152 return ERROR_SUCCESS;
3154 deformat_string( package, key_str, &deformated_key );
3155 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3156 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3157 strcpyW( ui_key_str, root_key_str );
3158 strcatW( ui_key_str, deformated_key );
3160 deformat_string( package, name, &deformated_name );
3162 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3163 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3164 msi_free( deformated_key );
3166 uirow = MSI_CreateRecord( 2 );
3167 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3168 MSI_RecordSetStringW( uirow, 2, deformated_name );
3169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3170 msiobj_release( &uirow->hdr );
3172 msi_free( ui_key_str );
3173 msi_free( deformated_name );
3174 return ERROR_SUCCESS;
3177 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3179 MSIPACKAGE *package = param;
3180 LPCWSTR component, name, key_str, root_key_str;
3181 LPWSTR deformated_key, deformated_name, ui_key_str;
3182 MSICOMPONENT *comp;
3183 MSIRECORD *uirow;
3184 BOOL delete_key = FALSE;
3185 HKEY hkey_root;
3186 UINT size;
3187 INT root;
3189 component = MSI_RecordGetString( row, 5 );
3190 comp = msi_get_loaded_component( package, component );
3191 if (!comp)
3192 return ERROR_SUCCESS;
3194 comp->Action = msi_get_component_action( package, comp );
3195 if (comp->Action != INSTALLSTATE_LOCAL)
3197 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3198 return ERROR_SUCCESS;
3201 if ((name = MSI_RecordGetString( row, 4 )))
3203 if (name[0] == '-' && !name[1])
3205 delete_key = TRUE;
3206 name = NULL;
3210 root = MSI_RecordGetInteger( row, 2 );
3211 key_str = MSI_RecordGetString( row, 3 );
3213 root_key_str = get_root_key( package, root, &hkey_root );
3214 if (!root_key_str)
3215 return ERROR_SUCCESS;
3217 deformat_string( package, key_str, &deformated_key );
3218 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3219 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3220 strcpyW( ui_key_str, root_key_str );
3221 strcatW( ui_key_str, deformated_key );
3223 deformat_string( package, name, &deformated_name );
3225 if (delete_key) delete_tree( comp, hkey_root, deformated_key );
3226 else delete_value( comp, hkey_root, deformated_key, deformated_name );
3227 msi_free( deformated_key );
3229 uirow = MSI_CreateRecord( 2 );
3230 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3231 MSI_RecordSetStringW( uirow, 2, deformated_name );
3232 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3233 msiobj_release( &uirow->hdr );
3235 msi_free( ui_key_str );
3236 msi_free( deformated_name );
3237 return ERROR_SUCCESS;
3240 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3242 static const WCHAR registry_query[] = {
3243 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3244 '`','R','e','g','i','s','t','r','y','`',0};
3245 static const WCHAR remove_registry_query[] = {
3246 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3247 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3248 MSIQUERY *view;
3249 UINT rc;
3251 if (package->script == SCRIPT_NONE)
3252 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveRegistryValues);
3254 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3255 if (rc == ERROR_SUCCESS)
3257 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3258 msiobj_release( &view->hdr );
3259 if (rc != ERROR_SUCCESS)
3260 return rc;
3262 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3263 if (rc == ERROR_SUCCESS)
3265 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3266 msiobj_release( &view->hdr );
3267 if (rc != ERROR_SUCCESS)
3268 return rc;
3270 return ERROR_SUCCESS;
3273 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3275 return ERROR_SUCCESS;
3279 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3281 static const WCHAR query[]= {
3282 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3283 '`','R','e','g','i','s','t','r','y','`',0};
3284 MSICOMPONENT *comp;
3285 DWORD total = 0, count = 0;
3286 MSIQUERY *view;
3287 MSIFEATURE *feature;
3288 MSIFILE *file;
3289 UINT rc;
3291 TRACE("InstallValidate\n");
3293 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3294 if (rc == ERROR_SUCCESS)
3296 rc = MSI_IterateRecords( view, &count, NULL, package );
3297 msiobj_release( &view->hdr );
3298 if (rc != ERROR_SUCCESS)
3299 return rc;
3300 total += count * REG_PROGRESS_VALUE;
3302 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3303 total += COMPONENT_PROGRESS_VALUE;
3305 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3306 total += file->FileSize;
3308 msi_ui_progress( package, 0, total, 0, 0 );
3310 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3312 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3313 debugstr_w(feature->Feature), feature->Installed,
3314 feature->ActionRequest, feature->Action);
3316 return ERROR_SUCCESS;
3319 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3321 MSIPACKAGE* package = param;
3322 LPCWSTR cond = NULL;
3323 LPCWSTR message = NULL;
3324 UINT r;
3326 static const WCHAR title[]=
3327 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3329 cond = MSI_RecordGetString(row,1);
3331 r = MSI_EvaluateConditionW(package,cond);
3332 if (r == MSICONDITION_FALSE)
3334 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3336 LPWSTR deformated;
3337 message = MSI_RecordGetString(row,2);
3338 deformat_string(package,message,&deformated);
3339 MessageBoxW(NULL,deformated,title,MB_OK);
3340 msi_free(deformated);
3343 return ERROR_INSTALL_FAILURE;
3346 return ERROR_SUCCESS;
3349 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3351 static const WCHAR query[] = {
3352 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3353 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3354 MSIQUERY *view;
3355 UINT rc;
3357 TRACE("Checking launch conditions\n");
3359 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3360 if (rc != ERROR_SUCCESS)
3361 return ERROR_SUCCESS;
3363 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3364 msiobj_release(&view->hdr);
3365 return rc;
3368 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3371 if (!cmp->KeyPath)
3372 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3374 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3376 static const WCHAR query[] = {
3377 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3378 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3379 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3380 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3381 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3382 MSIRECORD *row;
3383 UINT root, len;
3384 LPWSTR deformated, buffer, deformated_name;
3385 LPCWSTR key, name;
3387 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3388 if (!row)
3389 return NULL;
3391 root = MSI_RecordGetInteger(row,2);
3392 key = MSI_RecordGetString(row, 3);
3393 name = MSI_RecordGetString(row, 4);
3394 deformat_string(package, key , &deformated);
3395 deformat_string(package, name, &deformated_name);
3397 len = strlenW(deformated) + 6;
3398 if (deformated_name)
3399 len+=strlenW(deformated_name);
3401 buffer = msi_alloc( len *sizeof(WCHAR));
3403 if (deformated_name)
3404 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3405 else
3406 sprintfW(buffer,fmt,root,deformated);
3408 msi_free(deformated);
3409 msi_free(deformated_name);
3410 msiobj_release(&row->hdr);
3412 return buffer;
3414 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3416 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3417 return NULL;
3419 else
3421 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3423 if (file)
3424 return strdupW( file->TargetPath );
3426 return NULL;
3429 static HKEY openSharedDLLsKey(void)
3431 HKEY hkey=0;
3432 static const WCHAR path[] =
3433 {'S','o','f','t','w','a','r','e','\\',
3434 'M','i','c','r','o','s','o','f','t','\\',
3435 'W','i','n','d','o','w','s','\\',
3436 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3437 'S','h','a','r','e','d','D','L','L','s',0};
3439 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3440 return hkey;
3443 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3445 HKEY hkey;
3446 DWORD count=0;
3447 DWORD type;
3448 DWORD sz = sizeof(count);
3449 DWORD rc;
3451 hkey = openSharedDLLsKey();
3452 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3453 if (rc != ERROR_SUCCESS)
3454 count = 0;
3455 RegCloseKey(hkey);
3456 return count;
3459 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3461 HKEY hkey;
3463 hkey = openSharedDLLsKey();
3464 if (count > 0)
3465 msi_reg_set_val_dword( hkey, path, count );
3466 else
3467 RegDeleteValueW(hkey,path);
3468 RegCloseKey(hkey);
3469 return count;
3472 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3474 MSIFEATURE *feature;
3475 INT count = 0;
3476 BOOL write = FALSE;
3478 /* only refcount DLLs */
3479 if (comp->KeyPath == NULL ||
3480 comp->assembly ||
3481 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3482 comp->Attributes & msidbComponentAttributesODBCDataSource)
3483 write = FALSE;
3484 else
3486 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3487 write = (count > 0);
3489 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3490 write = TRUE;
3493 /* increment counts */
3494 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3496 ComponentList *cl;
3498 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3499 continue;
3501 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3503 if ( cl->component == comp )
3504 count++;
3508 /* decrement counts */
3509 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3511 ComponentList *cl;
3513 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3514 continue;
3516 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3518 if ( cl->component == comp )
3519 count--;
3523 /* ref count all the files in the component */
3524 if (write)
3526 MSIFILE *file;
3528 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3530 if (file->Component == comp)
3531 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3535 /* add a count for permanent */
3536 if (comp->Attributes & msidbComponentAttributesPermanent)
3537 count ++;
3539 comp->RefCount = count;
3541 if (write)
3542 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3545 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3547 if (comp->assembly)
3549 const WCHAR prefixW[] = {'<','\\',0};
3550 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3551 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3553 if (keypath)
3555 strcpyW( keypath, prefixW );
3556 strcatW( keypath, comp->assembly->display_name );
3558 return keypath;
3560 return resolve_keypath( package, comp );
3563 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3565 WCHAR squashed_pc[SQUASHED_GUID_SIZE], squashed_cc[SQUASHED_GUID_SIZE];
3566 UINT rc;
3567 MSICOMPONENT *comp;
3568 HKEY hkey;
3570 TRACE("\n");
3572 msi_set_sourcedir_props(package, FALSE);
3574 if (package->script == SCRIPT_NONE)
3575 return msi_schedule_action(package, SCRIPT_INSTALL, szProcessComponents);
3577 squash_guid( package->ProductCode, squashed_pc );
3579 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3581 MSIRECORD *uirow;
3582 INSTALLSTATE action;
3584 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3585 if (!comp->ComponentId)
3586 continue;
3588 squash_guid( comp->ComponentId, squashed_cc );
3589 msi_free( comp->FullKeypath );
3590 comp->FullKeypath = build_full_keypath( package, comp );
3592 ACTION_RefCountComponent( package, comp );
3594 if (package->need_rollback) action = comp->Installed;
3595 else action = comp->ActionRequest;
3597 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3598 debugstr_w(comp->Component), debugstr_w(squashed_cc),
3599 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3601 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3603 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3604 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3605 else
3606 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3608 if (rc != ERROR_SUCCESS)
3609 continue;
3611 if (comp->Attributes & msidbComponentAttributesPermanent)
3613 static const WCHAR szPermKey[] =
3614 { '0','0','0','0','0','0','0','0','0','0','0','0',
3615 '0','0','0','0','0','0','0','0','0','0','0','0',
3616 '0','0','0','0','0','0','0','0',0 };
3618 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3620 if (action == INSTALLSTATE_LOCAL)
3621 msi_reg_set_val_str( hkey, squashed_pc, comp->FullKeypath );
3622 else
3624 MSIFILE *file;
3625 MSIRECORD *row;
3626 LPWSTR ptr, ptr2;
3627 WCHAR source[MAX_PATH];
3628 WCHAR base[MAX_PATH];
3629 LPWSTR sourcepath;
3631 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3632 static const WCHAR query[] = {
3633 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3634 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3635 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3636 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3637 '`','D','i','s','k','I','d','`',0};
3639 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3640 continue;
3642 if (!(row = MSI_QueryGetRecord(package->db, query, file->Sequence)))
3643 return ERROR_FUNCTION_FAILED;
3645 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3646 ptr2 = strrchrW(source, '\\') + 1;
3647 msiobj_release(&row->hdr);
3649 lstrcpyW(base, package->PackagePath);
3650 ptr = strrchrW(base, '\\');
3651 *(ptr + 1) = '\0';
3653 sourcepath = msi_resolve_file_source(package, file);
3654 ptr = sourcepath + lstrlenW(base);
3655 lstrcpyW(ptr2, ptr);
3656 msi_free(sourcepath);
3658 msi_reg_set_val_str( hkey, squashed_pc, source );
3660 RegCloseKey(hkey);
3662 else if (action == INSTALLSTATE_ABSENT)
3664 if (comp->num_clients <= 0)
3666 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3667 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3668 else
3669 rc = MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3671 if (rc != ERROR_SUCCESS) WARN( "failed to delete component key %u\n", rc );
3673 else
3675 LONG res;
3677 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3678 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE );
3679 else
3680 rc = MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE );
3682 if (rc != ERROR_SUCCESS)
3684 WARN( "failed to open component key %u\n", rc );
3685 continue;
3687 res = RegDeleteValueW( hkey, squashed_pc );
3688 RegCloseKey(hkey);
3689 if (res) WARN( "failed to delete component value %d\n", res );
3693 /* UI stuff */
3694 uirow = MSI_CreateRecord(3);
3695 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3696 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3697 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3698 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
3699 msiobj_release( &uirow->hdr );
3701 return ERROR_SUCCESS;
3704 typedef struct {
3705 CLSID clsid;
3706 LPWSTR source;
3708 LPWSTR path;
3709 ITypeLib *ptLib;
3710 } typelib_struct;
3712 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3713 LPWSTR lpszName, LONG_PTR lParam)
3715 TLIBATTR *attr;
3716 typelib_struct *tl_struct = (typelib_struct*) lParam;
3717 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3718 int sz;
3719 HRESULT res;
3721 if (!IS_INTRESOURCE(lpszName))
3723 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3724 return TRUE;
3727 sz = strlenW(tl_struct->source)+4;
3728 sz *= sizeof(WCHAR);
3730 if ((INT_PTR)lpszName == 1)
3731 tl_struct->path = strdupW(tl_struct->source);
3732 else
3734 tl_struct->path = msi_alloc(sz);
3735 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3738 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3739 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3740 if (FAILED(res))
3742 msi_free(tl_struct->path);
3743 tl_struct->path = NULL;
3745 return TRUE;
3748 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3749 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3751 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3752 return FALSE;
3755 msi_free(tl_struct->path);
3756 tl_struct->path = NULL;
3758 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3759 ITypeLib_Release(tl_struct->ptLib);
3761 return TRUE;
3764 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3766 MSIPACKAGE* package = param;
3767 LPCWSTR component;
3768 MSICOMPONENT *comp;
3769 MSIFILE *file;
3770 typelib_struct tl_struct;
3771 ITypeLib *tlib;
3772 HMODULE module;
3773 HRESULT hr;
3775 component = MSI_RecordGetString(row,3);
3776 comp = msi_get_loaded_component(package,component);
3777 if (!comp)
3778 return ERROR_SUCCESS;
3780 comp->Action = msi_get_component_action( package, comp );
3781 if (comp->Action != INSTALLSTATE_LOCAL)
3783 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3784 return ERROR_SUCCESS;
3787 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3789 TRACE("component has no key path\n");
3790 return ERROR_SUCCESS;
3792 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3794 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3795 if (module)
3797 LPCWSTR guid;
3798 guid = MSI_RecordGetString(row,1);
3799 CLSIDFromString( guid, &tl_struct.clsid);
3800 tl_struct.source = strdupW( file->TargetPath );
3801 tl_struct.path = NULL;
3803 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3804 (LONG_PTR)&tl_struct);
3806 if (tl_struct.path)
3808 LPCWSTR helpid, help_path = NULL;
3809 HRESULT res;
3811 helpid = MSI_RecordGetString(row,6);
3813 if (helpid) help_path = msi_get_target_folder( package, helpid );
3814 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3816 if (FAILED(res))
3817 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3818 else
3819 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3821 ITypeLib_Release(tl_struct.ptLib);
3822 msi_free(tl_struct.path);
3824 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3826 FreeLibrary(module);
3827 msi_free(tl_struct.source);
3829 else
3831 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3832 if (FAILED(hr))
3834 ERR("Failed to load type library: %08x\n", hr);
3835 return ERROR_INSTALL_FAILURE;
3838 ITypeLib_Release(tlib);
3841 return ERROR_SUCCESS;
3844 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3846 static const WCHAR query[] = {
3847 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3848 '`','T','y','p','e','L','i','b','`',0};
3849 MSIQUERY *view;
3850 UINT rc;
3852 if (package->script == SCRIPT_NONE)
3853 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterTypeLibraries);
3855 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3856 if (rc != ERROR_SUCCESS)
3857 return ERROR_SUCCESS;
3859 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3860 msiobj_release(&view->hdr);
3861 return rc;
3864 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3866 MSIPACKAGE *package = param;
3867 LPCWSTR component, guid;
3868 MSICOMPONENT *comp;
3869 GUID libid;
3870 UINT version;
3871 LCID language;
3872 SYSKIND syskind;
3873 HRESULT hr;
3875 component = MSI_RecordGetString( row, 3 );
3876 comp = msi_get_loaded_component( package, component );
3877 if (!comp)
3878 return ERROR_SUCCESS;
3880 comp->Action = msi_get_component_action( package, comp );
3881 if (comp->Action != INSTALLSTATE_ABSENT)
3883 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3884 return ERROR_SUCCESS;
3886 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
3888 guid = MSI_RecordGetString( row, 1 );
3889 CLSIDFromString( guid, &libid );
3890 version = MSI_RecordGetInteger( row, 4 );
3891 language = MSI_RecordGetInteger( row, 2 );
3893 #ifdef _WIN64
3894 syskind = SYS_WIN64;
3895 #else
3896 syskind = SYS_WIN32;
3897 #endif
3899 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3900 if (FAILED(hr))
3902 WARN("Failed to unregister typelib: %08x\n", hr);
3905 return ERROR_SUCCESS;
3908 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3910 static const WCHAR query[] = {
3911 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3912 '`','T','y','p','e','L','i','b','`',0};
3913 MSIQUERY *view;
3914 UINT rc;
3916 if (package->script == SCRIPT_NONE)
3917 return msi_schedule_action(package, SCRIPT_INSTALL, szUnregisterTypeLibraries);
3919 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3920 if (rc != ERROR_SUCCESS)
3921 return ERROR_SUCCESS;
3923 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3924 msiobj_release( &view->hdr );
3925 return rc;
3928 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3930 static const WCHAR szlnk[] = {'.','l','n','k',0};
3931 LPCWSTR directory, extension, link_folder;
3932 LPWSTR link_file, filename;
3934 directory = MSI_RecordGetString( row, 2 );
3935 link_folder = msi_get_target_folder( package, directory );
3936 if (!link_folder)
3938 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3939 return NULL;
3941 /* may be needed because of a bug somewhere else */
3942 msi_create_full_path( link_folder );
3944 filename = msi_dup_record_field( row, 3 );
3945 msi_reduce_to_long_filename( filename );
3947 extension = strrchrW( filename, '.' );
3948 if (!extension || strcmpiW( extension, szlnk ))
3950 int len = strlenW( filename );
3951 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3952 memcpy( filename + len, szlnk, sizeof(szlnk) );
3954 link_file = msi_build_directory_name( 2, link_folder, filename );
3955 msi_free( filename );
3957 return link_file;
3960 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3962 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3963 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3964 WCHAR *folder, *dest, *path;
3966 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3967 folder = msi_dup_property( package->db, szWindowsFolder );
3968 else
3970 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3971 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3972 msi_free( appdata );
3974 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3975 msi_create_full_path( dest );
3976 path = msi_build_directory_name( 2, dest, icon_name );
3977 msi_free( folder );
3978 msi_free( dest );
3979 return path;
3982 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3984 MSIPACKAGE *package = param;
3985 LPWSTR link_file, deformated, path;
3986 LPCWSTR component, target;
3987 MSICOMPONENT *comp;
3988 IShellLinkW *sl = NULL;
3989 IPersistFile *pf = NULL;
3990 HRESULT res;
3992 component = MSI_RecordGetString(row, 4);
3993 comp = msi_get_loaded_component(package, component);
3994 if (!comp)
3995 return ERROR_SUCCESS;
3997 comp->Action = msi_get_component_action( package, comp );
3998 if (comp->Action != INSTALLSTATE_LOCAL)
4000 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4001 return ERROR_SUCCESS;
4003 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4005 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
4006 &IID_IShellLinkW, (LPVOID *) &sl );
4008 if (FAILED( res ))
4010 ERR("CLSID_ShellLink not available\n");
4011 goto err;
4014 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
4015 if (FAILED( res ))
4017 ERR("QueryInterface(IID_IPersistFile) failed\n");
4018 goto err;
4021 target = MSI_RecordGetString(row, 5);
4022 if (strchrW(target, '['))
4024 deformat_string( package, target, &path );
4025 TRACE("target path is %s\n", debugstr_w(path));
4026 IShellLinkW_SetPath( sl, path );
4027 msi_free( path );
4029 else
4031 FIXME("poorly handled shortcut format, advertised shortcut\n");
4032 path = resolve_keypath( package, comp );
4033 IShellLinkW_SetPath( sl, path );
4034 msi_free( path );
4037 if (!MSI_RecordIsNull(row,6))
4039 LPCWSTR arguments = MSI_RecordGetString(row, 6);
4040 deformat_string(package, arguments, &deformated);
4041 IShellLinkW_SetArguments(sl,deformated);
4042 msi_free(deformated);
4045 if (!MSI_RecordIsNull(row,7))
4047 LPCWSTR description = MSI_RecordGetString(row, 7);
4048 IShellLinkW_SetDescription(sl, description);
4051 if (!MSI_RecordIsNull(row,8))
4052 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
4054 if (!MSI_RecordIsNull(row,9))
4056 INT index;
4057 LPCWSTR icon = MSI_RecordGetString(row, 9);
4059 path = msi_build_icon_path(package, icon);
4060 index = MSI_RecordGetInteger(row,10);
4062 /* no value means 0 */
4063 if (index == MSI_NULL_INTEGER)
4064 index = 0;
4066 IShellLinkW_SetIconLocation(sl, path, index);
4067 msi_free(path);
4070 if (!MSI_RecordIsNull(row,11))
4071 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
4073 if (!MSI_RecordIsNull(row,12))
4075 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
4076 full_path = msi_get_target_folder( package, wkdir );
4077 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
4079 link_file = get_link_file(package, row);
4081 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
4082 IPersistFile_Save(pf, link_file, FALSE);
4083 msi_free(link_file);
4085 err:
4086 if (pf)
4087 IPersistFile_Release( pf );
4088 if (sl)
4089 IShellLinkW_Release( sl );
4091 return ERROR_SUCCESS;
4094 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
4096 static const WCHAR query[] = {
4097 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4098 '`','S','h','o','r','t','c','u','t','`',0};
4099 MSIQUERY *view;
4100 HRESULT res;
4101 UINT rc;
4103 if (package->script == SCRIPT_NONE)
4104 return msi_schedule_action(package, SCRIPT_INSTALL, szCreateShortcuts);
4106 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4107 if (rc != ERROR_SUCCESS)
4108 return ERROR_SUCCESS;
4110 res = CoInitialize( NULL );
4112 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4113 msiobj_release(&view->hdr);
4115 if (SUCCEEDED(res)) CoUninitialize();
4116 return rc;
4119 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4121 MSIPACKAGE *package = param;
4122 LPWSTR link_file;
4123 LPCWSTR component;
4124 MSICOMPONENT *comp;
4126 component = MSI_RecordGetString( row, 4 );
4127 comp = msi_get_loaded_component( package, component );
4128 if (!comp)
4129 return ERROR_SUCCESS;
4131 comp->Action = msi_get_component_action( package, comp );
4132 if (comp->Action != INSTALLSTATE_ABSENT)
4134 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4135 return ERROR_SUCCESS;
4137 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
4139 link_file = get_link_file( package, row );
4141 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4142 if (!DeleteFileW( link_file ))
4144 WARN("Failed to remove shortcut file %u\n", GetLastError());
4146 msi_free( link_file );
4148 return ERROR_SUCCESS;
4151 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4153 static const WCHAR query[] = {
4154 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4155 '`','S','h','o','r','t','c','u','t','`',0};
4156 MSIQUERY *view;
4157 UINT rc;
4159 if (package->script == SCRIPT_NONE)
4160 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveShortcuts);
4162 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4163 if (rc != ERROR_SUCCESS)
4164 return ERROR_SUCCESS;
4166 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4167 msiobj_release( &view->hdr );
4168 return rc;
4171 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4173 MSIPACKAGE* package = param;
4174 HANDLE the_file;
4175 LPWSTR FilePath;
4176 LPCWSTR FileName;
4177 CHAR buffer[1024];
4178 DWORD sz;
4179 UINT rc;
4181 FileName = MSI_RecordGetString(row,1);
4182 if (!FileName)
4184 ERR("Unable to get FileName\n");
4185 return ERROR_SUCCESS;
4188 FilePath = msi_build_icon_path(package, FileName);
4190 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4192 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4193 FILE_ATTRIBUTE_NORMAL, NULL);
4195 if (the_file == INVALID_HANDLE_VALUE)
4197 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4198 msi_free(FilePath);
4199 return ERROR_SUCCESS;
4204 DWORD write;
4205 sz = 1024;
4206 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4207 if (rc != ERROR_SUCCESS)
4209 ERR("Failed to get stream\n");
4210 DeleteFileW(FilePath);
4211 break;
4213 WriteFile(the_file,buffer,sz,&write,NULL);
4214 } while (sz == 1024);
4216 msi_free(FilePath);
4217 CloseHandle(the_file);
4219 return ERROR_SUCCESS;
4222 static UINT msi_publish_icons(MSIPACKAGE *package)
4224 static const WCHAR query[]= {
4225 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4226 '`','I','c','o','n','`',0};
4227 MSIQUERY *view;
4228 UINT r;
4230 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4231 if (r == ERROR_SUCCESS)
4233 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4234 msiobj_release(&view->hdr);
4235 if (r != ERROR_SUCCESS)
4236 return r;
4238 return ERROR_SUCCESS;
4241 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4243 UINT r;
4244 HKEY source;
4245 LPWSTR buffer;
4246 MSIMEDIADISK *disk;
4247 MSISOURCELISTINFO *info;
4249 r = RegCreateKeyW(hkey, szSourceList, &source);
4250 if (r != ERROR_SUCCESS)
4251 return r;
4253 RegCloseKey(source);
4255 buffer = strrchrW(package->PackagePath, '\\') + 1;
4256 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4257 package->Context, MSICODE_PRODUCT,
4258 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4259 if (r != ERROR_SUCCESS)
4260 return r;
4262 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4263 package->Context, MSICODE_PRODUCT,
4264 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4265 if (r != ERROR_SUCCESS)
4266 return r;
4268 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4269 package->Context, MSICODE_PRODUCT,
4270 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4271 if (r != ERROR_SUCCESS)
4272 return r;
4274 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4276 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4277 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4278 info->options, info->value);
4279 else
4280 MsiSourceListSetInfoW(package->ProductCode, NULL,
4281 info->context, info->options,
4282 info->property, info->value);
4285 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4287 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4288 disk->context, disk->options,
4289 disk->disk_id, disk->volume_label, disk->disk_prompt);
4292 return ERROR_SUCCESS;
4295 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4297 static const WCHAR szARPProductIcon[] =
4298 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4299 static const WCHAR szAssignment[] =
4300 {'A','s','s','i','g','n','m','e','n','t',0};
4301 static const WCHAR szAdvertiseFlags[] =
4302 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4303 static const WCHAR szClients[] =
4304 {'C','l','i','e','n','t','s',0};
4305 static const WCHAR szColon[] = {':',0};
4306 WCHAR *buffer, *ptr, *guids, packcode[SQUASHED_GUID_SIZE];
4307 DWORD langid;
4309 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4310 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4311 msi_free(buffer);
4313 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4314 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4316 /* FIXME */
4317 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4319 buffer = msi_dup_property(package->db, szARPProductIcon);
4320 if (buffer)
4322 LPWSTR path = msi_build_icon_path(package, buffer);
4323 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4324 msi_free(path);
4325 msi_free(buffer);
4328 buffer = msi_dup_property(package->db, szProductVersion);
4329 if (buffer)
4331 DWORD verdword = msi_version_str_to_dword(buffer);
4332 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4333 msi_free(buffer);
4336 msi_reg_set_val_dword(hkey, szAssignment, 0);
4337 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4338 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4339 msi_reg_set_val_str(hkey, szClients, szColon);
4341 if (!(guids = msi_get_package_code(package->db))) return ERROR_OUTOFMEMORY;
4342 if ((ptr = strchrW(guids, ';'))) *ptr = 0;
4343 squash_guid(guids, packcode);
4344 msi_free( guids);
4345 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4347 return ERROR_SUCCESS;
4350 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4352 UINT r;
4353 HKEY hkey;
4354 WCHAR *upgrade, squashed_pc[SQUASHED_GUID_SIZE];
4356 upgrade = msi_dup_property(package->db, szUpgradeCode);
4357 if (!upgrade)
4358 return ERROR_SUCCESS;
4360 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4361 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4362 else
4363 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4365 if (r != ERROR_SUCCESS)
4367 WARN("failed to open upgrade code key\n");
4368 msi_free(upgrade);
4369 return ERROR_SUCCESS;
4371 squash_guid(package->ProductCode, squashed_pc);
4372 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4373 RegCloseKey(hkey);
4374 msi_free(upgrade);
4375 return ERROR_SUCCESS;
4378 static BOOL msi_check_publish(MSIPACKAGE *package)
4380 MSIFEATURE *feature;
4382 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4384 feature->Action = msi_get_feature_action( package, feature );
4385 if (feature->Action == INSTALLSTATE_LOCAL || feature->Action == INSTALLSTATE_SOURCE)
4386 return TRUE;
4389 return FALSE;
4392 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4394 MSIFEATURE *feature;
4396 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4398 feature->Action = msi_get_feature_action( package, feature );
4399 if (feature->Action != INSTALLSTATE_ABSENT)
4400 return FALSE;
4403 return TRUE;
4406 static UINT msi_publish_patches( MSIPACKAGE *package )
4408 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4409 WCHAR patch_squashed[GUID_SIZE];
4410 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4411 LONG res;
4412 MSIPATCHINFO *patch;
4413 UINT r;
4414 WCHAR *p, *all_patches = NULL;
4415 DWORD len = 0;
4417 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4418 if (r != ERROR_SUCCESS)
4419 return ERROR_FUNCTION_FAILED;
4421 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4422 if (res != ERROR_SUCCESS)
4424 r = ERROR_FUNCTION_FAILED;
4425 goto done;
4428 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4429 if (r != ERROR_SUCCESS)
4430 goto done;
4432 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4434 squash_guid( patch->patchcode, patch_squashed );
4435 len += strlenW( patch_squashed ) + 1;
4438 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4439 if (!all_patches)
4440 goto done;
4442 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4444 HKEY patch_key;
4446 squash_guid( patch->patchcode, p );
4447 p += strlenW( p ) + 1;
4449 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4450 (const BYTE *)patch->transforms,
4451 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4452 if (res != ERROR_SUCCESS)
4453 goto done;
4455 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4456 if (r != ERROR_SUCCESS)
4457 goto done;
4459 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4460 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4461 RegCloseKey( patch_key );
4462 if (res != ERROR_SUCCESS)
4463 goto done;
4465 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4467 res = GetLastError();
4468 ERR("Unable to copy patch package %d\n", res);
4469 goto done;
4471 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4472 if (res != ERROR_SUCCESS)
4473 goto done;
4475 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state,
4476 sizeof(patch->state) );
4477 if (res != ERROR_SUCCESS)
4479 RegCloseKey( patch_key );
4480 goto done;
4483 res = RegSetValueExW( patch_key, szUninstallable, 0, REG_DWORD, (const BYTE *)&patch->uninstallable,
4484 sizeof(patch->uninstallable) );
4485 RegCloseKey( patch_key );
4486 if (res != ERROR_SUCCESS)
4487 goto done;
4490 all_patches[len] = 0;
4491 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4492 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4493 if (res != ERROR_SUCCESS)
4494 goto done;
4496 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4497 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4498 if (res != ERROR_SUCCESS)
4499 r = ERROR_FUNCTION_FAILED;
4501 done:
4502 RegCloseKey( product_patches_key );
4503 RegCloseKey( patches_key );
4504 RegCloseKey( product_key );
4505 msi_free( all_patches );
4506 return r;
4509 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4511 UINT rc;
4512 HKEY hukey = NULL, hudkey = NULL;
4513 MSIRECORD *uirow;
4514 BOOL republish = FALSE;
4516 if (package->script == SCRIPT_NONE)
4517 return msi_schedule_action(package, SCRIPT_INSTALL, szPublishProduct);
4519 if (!list_empty(&package->patches))
4521 rc = msi_publish_patches(package);
4522 if (rc != ERROR_SUCCESS)
4523 goto end;
4526 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4527 &hukey, FALSE);
4528 if (rc == ERROR_SUCCESS)
4530 WCHAR *package_code;
4532 package_code = msi_reg_get_val_str(hukey, INSTALLPROPERTY_PACKAGECODEW);
4533 if (package_code)
4535 WCHAR *guid;
4537 guid = msi_get_package_code(package->db);
4538 if (guid)
4540 WCHAR packed[SQUASHED_GUID_SIZE];
4542 squash_guid(guid, packed);
4543 msi_free(guid);
4544 if (!strcmpW(packed, package_code))
4546 TRACE("re-publishing product - new package\n");
4547 republish = TRUE;
4550 msi_free(package_code);
4554 /* FIXME: also need to publish if the product is in advertise mode */
4555 if (!republish && !msi_check_publish(package))
4557 if (hukey)
4558 RegCloseKey(hukey);
4559 return ERROR_SUCCESS;
4562 if (!hukey)
4564 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4565 &hukey, TRUE);
4566 if (rc != ERROR_SUCCESS)
4567 goto end;
4570 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4571 NULL, &hudkey, TRUE);
4572 if (rc != ERROR_SUCCESS)
4573 goto end;
4575 rc = msi_publish_upgrade_code(package);
4576 if (rc != ERROR_SUCCESS)
4577 goto end;
4579 rc = msi_publish_product_properties(package, hukey);
4580 if (rc != ERROR_SUCCESS)
4581 goto end;
4583 rc = msi_publish_sourcelist(package, hukey);
4584 if (rc != ERROR_SUCCESS)
4585 goto end;
4587 rc = msi_publish_icons(package);
4589 end:
4590 uirow = MSI_CreateRecord( 1 );
4591 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4592 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4593 msiobj_release( &uirow->hdr );
4595 RegCloseKey(hukey);
4596 RegCloseKey(hudkey);
4597 return rc;
4600 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4602 WCHAR *filename, *ptr, *folder, *ret;
4603 const WCHAR *dirprop;
4605 filename = msi_dup_record_field( row, 2 );
4606 if (filename && (ptr = strchrW( filename, '|' )))
4607 ptr++;
4608 else
4609 ptr = filename;
4611 dirprop = MSI_RecordGetString( row, 3 );
4612 if (dirprop)
4614 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4615 if (!folder) folder = msi_dup_property( package->db, dirprop );
4617 else
4618 folder = msi_dup_property( package->db, szWindowsFolder );
4620 if (!folder)
4622 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4623 msi_free( filename );
4624 return NULL;
4627 ret = msi_build_directory_name( 2, folder, ptr );
4629 msi_free( filename );
4630 msi_free( folder );
4631 return ret;
4634 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4636 MSIPACKAGE *package = param;
4637 LPCWSTR component, section, key, value, identifier;
4638 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4639 MSIRECORD * uirow;
4640 INT action;
4641 MSICOMPONENT *comp;
4643 component = MSI_RecordGetString(row, 8);
4644 comp = msi_get_loaded_component(package,component);
4645 if (!comp)
4646 return ERROR_SUCCESS;
4648 comp->Action = msi_get_component_action( package, comp );
4649 if (comp->Action != INSTALLSTATE_LOCAL)
4651 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4652 return ERROR_SUCCESS;
4655 identifier = MSI_RecordGetString(row,1);
4656 section = MSI_RecordGetString(row,4);
4657 key = MSI_RecordGetString(row,5);
4658 value = MSI_RecordGetString(row,6);
4659 action = MSI_RecordGetInteger(row,7);
4661 deformat_string(package,section,&deformated_section);
4662 deformat_string(package,key,&deformated_key);
4663 deformat_string(package,value,&deformated_value);
4665 fullname = get_ini_file_name(package, row);
4667 if (action == 0)
4669 TRACE("Adding value %s to section %s in %s\n",
4670 debugstr_w(deformated_key), debugstr_w(deformated_section),
4671 debugstr_w(fullname));
4672 WritePrivateProfileStringW(deformated_section, deformated_key,
4673 deformated_value, fullname);
4675 else if (action == 1)
4677 WCHAR returned[10];
4678 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4679 returned, 10, fullname);
4680 if (returned[0] == 0)
4682 TRACE("Adding value %s to section %s in %s\n",
4683 debugstr_w(deformated_key), debugstr_w(deformated_section),
4684 debugstr_w(fullname));
4686 WritePrivateProfileStringW(deformated_section, deformated_key,
4687 deformated_value, fullname);
4690 else if (action == 3)
4691 FIXME("Append to existing section not yet implemented\n");
4693 uirow = MSI_CreateRecord(4);
4694 MSI_RecordSetStringW(uirow,1,identifier);
4695 MSI_RecordSetStringW(uirow,2,deformated_section);
4696 MSI_RecordSetStringW(uirow,3,deformated_key);
4697 MSI_RecordSetStringW(uirow,4,deformated_value);
4698 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4699 msiobj_release( &uirow->hdr );
4701 msi_free(fullname);
4702 msi_free(deformated_key);
4703 msi_free(deformated_value);
4704 msi_free(deformated_section);
4705 return ERROR_SUCCESS;
4708 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4710 static const WCHAR query[] = {
4711 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4712 '`','I','n','i','F','i','l','e','`',0};
4713 MSIQUERY *view;
4714 UINT rc;
4716 if (package->script == SCRIPT_NONE)
4717 return msi_schedule_action(package, SCRIPT_INSTALL, szWriteIniValues);
4719 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4720 if (rc != ERROR_SUCCESS)
4721 return ERROR_SUCCESS;
4723 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4724 msiobj_release(&view->hdr);
4725 return rc;
4728 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4730 MSIPACKAGE *package = param;
4731 LPCWSTR component, section, key, value, identifier;
4732 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4733 MSICOMPONENT *comp;
4734 MSIRECORD *uirow;
4735 INT action;
4737 component = MSI_RecordGetString( row, 8 );
4738 comp = msi_get_loaded_component( package, component );
4739 if (!comp)
4740 return ERROR_SUCCESS;
4742 comp->Action = msi_get_component_action( package, comp );
4743 if (comp->Action != INSTALLSTATE_ABSENT)
4745 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4746 return ERROR_SUCCESS;
4749 identifier = MSI_RecordGetString( row, 1 );
4750 section = MSI_RecordGetString( row, 4 );
4751 key = MSI_RecordGetString( row, 5 );
4752 value = MSI_RecordGetString( row, 6 );
4753 action = MSI_RecordGetInteger( row, 7 );
4755 deformat_string( package, section, &deformated_section );
4756 deformat_string( package, key, &deformated_key );
4757 deformat_string( package, value, &deformated_value );
4759 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4761 filename = get_ini_file_name( package, row );
4763 TRACE("Removing key %s from section %s in %s\n",
4764 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4766 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4768 WARN("Unable to remove key %u\n", GetLastError());
4770 msi_free( filename );
4772 else
4773 FIXME("Unsupported action %d\n", action);
4776 uirow = MSI_CreateRecord( 4 );
4777 MSI_RecordSetStringW( uirow, 1, identifier );
4778 MSI_RecordSetStringW( uirow, 2, deformated_section );
4779 MSI_RecordSetStringW( uirow, 3, deformated_key );
4780 MSI_RecordSetStringW( uirow, 4, deformated_value );
4781 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4782 msiobj_release( &uirow->hdr );
4784 msi_free( deformated_key );
4785 msi_free( deformated_value );
4786 msi_free( deformated_section );
4787 return ERROR_SUCCESS;
4790 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4792 MSIPACKAGE *package = param;
4793 LPCWSTR component, section, key, value, identifier;
4794 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4795 MSICOMPONENT *comp;
4796 MSIRECORD *uirow;
4797 INT action;
4799 component = MSI_RecordGetString( row, 8 );
4800 comp = msi_get_loaded_component( package, component );
4801 if (!comp)
4802 return ERROR_SUCCESS;
4804 comp->Action = msi_get_component_action( package, comp );
4805 if (comp->Action != INSTALLSTATE_LOCAL)
4807 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4808 return ERROR_SUCCESS;
4811 identifier = MSI_RecordGetString( row, 1 );
4812 section = MSI_RecordGetString( row, 4 );
4813 key = MSI_RecordGetString( row, 5 );
4814 value = MSI_RecordGetString( row, 6 );
4815 action = MSI_RecordGetInteger( row, 7 );
4817 deformat_string( package, section, &deformated_section );
4818 deformat_string( package, key, &deformated_key );
4819 deformat_string( package, value, &deformated_value );
4821 if (action == msidbIniFileActionRemoveLine)
4823 filename = get_ini_file_name( package, row );
4825 TRACE("Removing key %s from section %s in %s\n",
4826 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4828 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4830 WARN("Unable to remove key %u\n", GetLastError());
4832 msi_free( filename );
4834 else
4835 FIXME("Unsupported action %d\n", action);
4837 uirow = MSI_CreateRecord( 4 );
4838 MSI_RecordSetStringW( uirow, 1, identifier );
4839 MSI_RecordSetStringW( uirow, 2, deformated_section );
4840 MSI_RecordSetStringW( uirow, 3, deformated_key );
4841 MSI_RecordSetStringW( uirow, 4, deformated_value );
4842 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4843 msiobj_release( &uirow->hdr );
4845 msi_free( deformated_key );
4846 msi_free( deformated_value );
4847 msi_free( deformated_section );
4848 return ERROR_SUCCESS;
4851 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4853 static const WCHAR query[] = {
4854 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4855 '`','I','n','i','F','i','l','e','`',0};
4856 static const WCHAR remove_query[] = {
4857 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4858 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4859 MSIQUERY *view;
4860 UINT rc;
4862 if (package->script == SCRIPT_NONE)
4863 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveIniValues);
4865 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4866 if (rc == ERROR_SUCCESS)
4868 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4869 msiobj_release( &view->hdr );
4870 if (rc != ERROR_SUCCESS)
4871 return rc;
4873 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4874 if (rc == ERROR_SUCCESS)
4876 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4877 msiobj_release( &view->hdr );
4878 if (rc != ERROR_SUCCESS)
4879 return rc;
4881 return ERROR_SUCCESS;
4884 static void register_dll( const WCHAR *dll, BOOL unregister )
4886 static const WCHAR regW[] =
4887 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4888 static const WCHAR unregW[] =
4889 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4890 PROCESS_INFORMATION pi;
4891 STARTUPINFOW si;
4892 WCHAR *cmd;
4894 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4896 if (unregister) sprintfW( cmd, unregW, dll );
4897 else sprintfW( cmd, regW, dll );
4899 memset( &si, 0, sizeof(STARTUPINFOW) );
4900 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4902 CloseHandle( pi.hThread );
4903 msi_dialog_check_messages( pi.hProcess );
4904 CloseHandle( pi.hProcess );
4906 msi_free( cmd );
4909 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4911 MSIPACKAGE *package = param;
4912 LPCWSTR filename;
4913 MSIFILE *file;
4914 MSIRECORD *uirow;
4916 filename = MSI_RecordGetString( row, 1 );
4917 file = msi_get_loaded_file( package, filename );
4918 if (!file)
4920 WARN("unable to find file %s\n", debugstr_w(filename));
4921 return ERROR_SUCCESS;
4923 file->Component->Action = msi_get_component_action( package, file->Component );
4924 if (file->Component->Action != INSTALLSTATE_LOCAL)
4926 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4927 return ERROR_SUCCESS;
4930 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4931 register_dll( file->TargetPath, FALSE );
4933 uirow = MSI_CreateRecord( 2 );
4934 MSI_RecordSetStringW( uirow, 1, file->File );
4935 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4936 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4937 msiobj_release( &uirow->hdr );
4939 return ERROR_SUCCESS;
4942 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4944 static const WCHAR query[] = {
4945 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4946 '`','S','e','l','f','R','e','g','`',0};
4947 MSIQUERY *view;
4948 UINT rc;
4950 if (package->script == SCRIPT_NONE)
4951 return msi_schedule_action(package, SCRIPT_INSTALL, szSelfRegModules);
4953 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4954 if (rc != ERROR_SUCCESS)
4955 return ERROR_SUCCESS;
4957 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4958 msiobj_release(&view->hdr);
4959 return rc;
4962 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4964 MSIPACKAGE *package = param;
4965 LPCWSTR filename;
4966 MSIFILE *file;
4967 MSIRECORD *uirow;
4969 filename = MSI_RecordGetString( row, 1 );
4970 file = msi_get_loaded_file( package, filename );
4971 if (!file)
4973 WARN("unable to find file %s\n", debugstr_w(filename));
4974 return ERROR_SUCCESS;
4976 file->Component->Action = msi_get_component_action( package, file->Component );
4977 if (file->Component->Action != INSTALLSTATE_ABSENT)
4979 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4980 return ERROR_SUCCESS;
4983 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4984 register_dll( file->TargetPath, TRUE );
4986 uirow = MSI_CreateRecord( 2 );
4987 MSI_RecordSetStringW( uirow, 1, file->File );
4988 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4989 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
4990 msiobj_release( &uirow->hdr );
4992 return ERROR_SUCCESS;
4995 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4997 static const WCHAR query[] = {
4998 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4999 '`','S','e','l','f','R','e','g','`',0};
5000 MSIQUERY *view;
5001 UINT rc;
5003 if (package->script == SCRIPT_NONE)
5004 return msi_schedule_action(package, SCRIPT_INSTALL, szSelfUnregModules);
5006 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5007 if (rc != ERROR_SUCCESS)
5008 return ERROR_SUCCESS;
5010 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
5011 msiobj_release( &view->hdr );
5012 return rc;
5015 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
5017 MSIFEATURE *feature;
5018 UINT rc;
5019 HKEY hkey = NULL, userdata = NULL;
5021 if (package->script == SCRIPT_NONE)
5022 return msi_schedule_action(package, SCRIPT_INSTALL, szPublishFeatures);
5024 if (!msi_check_publish(package))
5025 return ERROR_SUCCESS;
5027 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5028 &hkey, TRUE);
5029 if (rc != ERROR_SUCCESS)
5030 goto end;
5032 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5033 &userdata, TRUE);
5034 if (rc != ERROR_SUCCESS)
5035 goto end;
5037 /* here the guids are base 85 encoded */
5038 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5040 ComponentList *cl;
5041 LPWSTR data = NULL;
5042 GUID clsid;
5043 INT size;
5044 BOOL absent = FALSE;
5045 MSIRECORD *uirow;
5047 if (feature->Level <= 0) continue;
5048 if (feature->Action == INSTALLSTATE_UNKNOWN &&
5049 feature->Installed != INSTALLSTATE_ABSENT) continue;
5051 if (feature->Action != INSTALLSTATE_LOCAL &&
5052 feature->Action != INSTALLSTATE_SOURCE &&
5053 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
5055 size = 1;
5056 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5058 size += 21;
5060 if (feature->Feature_Parent)
5061 size += strlenW( feature->Feature_Parent )+2;
5063 data = msi_alloc(size * sizeof(WCHAR));
5065 data[0] = 0;
5066 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
5068 MSICOMPONENT* component = cl->component;
5069 WCHAR buf[21];
5071 buf[0] = 0;
5072 if (component->ComponentId)
5074 TRACE("From %s\n",debugstr_w(component->ComponentId));
5075 CLSIDFromString(component->ComponentId, &clsid);
5076 encode_base85_guid(&clsid,buf);
5077 TRACE("to %s\n",debugstr_w(buf));
5078 strcatW(data,buf);
5082 if (feature->Feature_Parent)
5084 static const WCHAR sep[] = {'\2',0};
5085 strcatW(data,sep);
5086 strcatW(data,feature->Feature_Parent);
5089 msi_reg_set_val_str( userdata, feature->Feature, data );
5090 msi_free(data);
5092 size = 0;
5093 if (feature->Feature_Parent)
5094 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
5095 if (!absent)
5097 size += sizeof(WCHAR);
5098 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5099 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
5101 else
5103 size += 2*sizeof(WCHAR);
5104 data = msi_alloc(size);
5105 data[0] = 0x6;
5106 data[1] = 0;
5107 if (feature->Feature_Parent)
5108 strcpyW( &data[1], feature->Feature_Parent );
5109 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
5110 (LPBYTE)data,size);
5111 msi_free(data);
5114 /* the UI chunk */
5115 uirow = MSI_CreateRecord( 1 );
5116 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5117 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5118 msiobj_release( &uirow->hdr );
5119 /* FIXME: call msi_ui_progress? */
5122 end:
5123 RegCloseKey(hkey);
5124 RegCloseKey(userdata);
5125 return rc;
5128 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
5130 UINT r;
5131 HKEY hkey;
5132 MSIRECORD *uirow;
5134 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
5136 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
5137 &hkey, FALSE);
5138 if (r == ERROR_SUCCESS)
5140 RegDeleteValueW(hkey, feature->Feature);
5141 RegCloseKey(hkey);
5144 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
5145 &hkey, FALSE);
5146 if (r == ERROR_SUCCESS)
5148 RegDeleteValueW(hkey, feature->Feature);
5149 RegCloseKey(hkey);
5152 uirow = MSI_CreateRecord( 1 );
5153 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5154 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5155 msiobj_release( &uirow->hdr );
5157 return ERROR_SUCCESS;
5160 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5162 MSIFEATURE *feature;
5164 if (package->script == SCRIPT_NONE)
5165 return msi_schedule_action(package, SCRIPT_INSTALL, szUnpublishFeatures);
5167 if (!msi_check_unpublish(package))
5168 return ERROR_SUCCESS;
5170 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5172 msi_unpublish_feature(package, feature);
5175 return ERROR_SUCCESS;
5178 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5180 static const WCHAR date_fmt[] =
5181 {'%','i','%','0','2','i','%','0','2','i',0};
5182 static const WCHAR szEstimatedSize[] =
5183 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5184 static const WCHAR szDisplayVersion[] =
5185 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5186 static const WCHAR szInstallSource[] =
5187 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5188 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5189 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5190 static const WCHAR szAuthorizedCDFPrefix[] =
5191 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5192 static const WCHAR szARPCONTACT[] =
5193 {'A','R','P','C','O','N','T','A','C','T',0};
5194 static const WCHAR szContact[] =
5195 {'C','o','n','t','a','c','t',0};
5196 static const WCHAR szARPCOMMENTS[] =
5197 {'A','R','P','C','O','M','M','E','N','T','S',0};
5198 static const WCHAR szComments[] =
5199 {'C','o','m','m','e','n','t','s',0};
5200 static const WCHAR szProductName[] =
5201 {'P','r','o','d','u','c','t','N','a','m','e',0};
5202 static const WCHAR szDisplayName[] =
5203 {'D','i','s','p','l','a','y','N','a','m','e',0};
5204 static const WCHAR szARPHELPLINK[] =
5205 {'A','R','P','H','E','L','P','L','I','N','K',0};
5206 static const WCHAR szHelpLink[] =
5207 {'H','e','l','p','L','i','n','k',0};
5208 static const WCHAR szARPHELPTELEPHONE[] =
5209 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5210 static const WCHAR szHelpTelephone[] =
5211 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5212 static const WCHAR szARPINSTALLLOCATION[] =
5213 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5214 static const WCHAR szManufacturer[] =
5215 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5216 static const WCHAR szPublisher[] =
5217 {'P','u','b','l','i','s','h','e','r',0};
5218 static const WCHAR szARPREADME[] =
5219 {'A','R','P','R','E','A','D','M','E',0};
5220 static const WCHAR szReadme[] =
5221 {'R','e','a','d','M','e',0};
5222 static const WCHAR szARPSIZE[] =
5223 {'A','R','P','S','I','Z','E',0};
5224 static const WCHAR szSize[] =
5225 {'S','i','z','e',0};
5226 static const WCHAR szARPURLINFOABOUT[] =
5227 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5228 static const WCHAR szURLInfoAbout[] =
5229 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5230 static const WCHAR szARPURLUPDATEINFO[] =
5231 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5232 static const WCHAR szURLUpdateInfo[] =
5233 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5234 static const WCHAR szARPSYSTEMCOMPONENT[] =
5235 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5236 static const WCHAR szSystemComponent[] =
5237 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5238 static const WCHAR szARPNOMODIFY[] =
5239 {'A','R','P','N','O','M','O','D','I','F','Y',0};
5240 static const WCHAR szNoModify[] =
5241 {'N','o','M','o','d','i','f','y',0};
5242 static const WCHAR szARPNOREMOVE[] =
5243 {'A','R','P','N','O','R','E','M','O','V','E',0};
5244 static const WCHAR szNoRemove[] =
5245 {'N','o','R','e','m','o','v','e',0};
5246 static const WCHAR szARPNOREPAIR[] =
5247 {'A','R','P','N','O','R','E','P','A','I','R',0};
5248 static const WCHAR szNoRepair[] =
5249 {'N','o','R','e','p','a','i','r',0};
5251 static const WCHAR *propval[] = {
5252 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5253 szARPCONTACT, szContact,
5254 szARPCOMMENTS, szComments,
5255 szProductName, szDisplayName,
5256 szARPHELPLINK, szHelpLink,
5257 szARPHELPTELEPHONE, szHelpTelephone,
5258 szARPINSTALLLOCATION, szInstallLocation,
5259 szSourceDir, szInstallSource,
5260 szManufacturer, szPublisher,
5261 szARPREADME, szReadme,
5262 szARPSIZE, szSize,
5263 szARPURLINFOABOUT, szURLInfoAbout,
5264 szARPURLUPDATEINFO, szURLUpdateInfo,
5265 NULL
5267 const WCHAR **p = propval;
5268 SYSTEMTIME systime;
5269 DWORD size, langid;
5270 WCHAR date[9], *val, *buffer;
5271 const WCHAR *prop, *key;
5273 while (*p)
5275 prop = *p++;
5276 key = *p++;
5277 val = msi_dup_property(package->db, prop);
5278 msi_reg_set_val_str(hkey, key, val);
5279 msi_free(val);
5282 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5283 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5285 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5288 if (msi_get_property_int( package->db, szARPNOREMOVE, 0 ))
5289 msi_reg_set_val_dword( hkey, szNoRemove, 1 );
5290 else
5292 static const WCHAR fmt_install[] =
5293 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5294 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5295 static const WCHAR fmt_uninstall[] =
5296 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5297 '/','X','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5298 static const WCHAR szModifyPath[] =
5299 {'M','o','d','i','f','y','P','a','t','h',0};
5300 static const WCHAR szUninstallString[] =
5301 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5302 const WCHAR *fmt = fmt_install;
5304 if (msi_get_property_int( package->db, szARPNOREPAIR, 0 ))
5305 msi_reg_set_val_dword( hkey, szNoRepair, 1 );
5307 if (msi_get_property_int( package->db, szARPNOMODIFY, 0 ))
5309 msi_reg_set_val_dword( hkey, szNoModify, 1 );
5310 fmt = fmt_uninstall;
5313 size = deformat_string(package, fmt, &buffer) * sizeof(WCHAR);
5314 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5315 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5316 msi_free(buffer);
5319 /* FIXME: Write real Estimated Size when we have it */
5320 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5322 GetLocalTime(&systime);
5323 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5324 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5326 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5327 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5329 buffer = msi_dup_property(package->db, szProductVersion);
5330 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5331 if (buffer)
5333 DWORD verdword = msi_version_str_to_dword(buffer);
5335 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5336 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5337 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5338 msi_free(buffer);
5341 return ERROR_SUCCESS;
5344 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5346 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5347 MSIRECORD *uirow;
5348 HKEY hkey, props, upgrade_key;
5349 UINT rc;
5351 if (package->script == SCRIPT_NONE)
5352 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterProduct);
5354 /* FIXME: also need to publish if the product is in advertise mode */
5355 if (!msi_get_property_int( package->db, szProductToBeRegistered, 0 )
5356 && !msi_check_publish(package))
5357 return ERROR_SUCCESS;
5359 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5360 if (rc != ERROR_SUCCESS)
5361 return rc;
5363 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5364 if (rc != ERROR_SUCCESS)
5365 goto done;
5367 rc = msi_publish_install_properties(package, hkey);
5368 if (rc != ERROR_SUCCESS)
5369 goto done;
5371 rc = msi_publish_install_properties(package, props);
5372 if (rc != ERROR_SUCCESS)
5373 goto done;
5375 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5376 if (upgrade_code)
5378 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5379 if (rc == ERROR_SUCCESS)
5381 squash_guid( package->ProductCode, squashed_pc );
5382 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5383 RegCloseKey( upgrade_key );
5385 msi_free( upgrade_code );
5387 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5388 package->delete_on_close = FALSE;
5390 done:
5391 uirow = MSI_CreateRecord( 1 );
5392 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5393 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5394 msiobj_release( &uirow->hdr );
5396 RegCloseKey(hkey);
5397 return ERROR_SUCCESS;
5400 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5402 return execute_script(package, SCRIPT_INSTALL);
5405 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5407 MSIPACKAGE *package = param;
5408 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5409 WCHAR *p, *icon_path;
5411 if (!icon) return ERROR_SUCCESS;
5412 if ((icon_path = msi_build_icon_path( package, icon )))
5414 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5415 DeleteFileW( icon_path );
5416 if ((p = strrchrW( icon_path, '\\' )))
5418 *p = 0;
5419 RemoveDirectoryW( icon_path );
5421 msi_free( icon_path );
5423 return ERROR_SUCCESS;
5426 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5428 static const WCHAR query[]= {
5429 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5430 MSIQUERY *view;
5431 UINT r;
5433 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5434 if (r == ERROR_SUCCESS)
5436 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5437 msiobj_release( &view->hdr );
5438 if (r != ERROR_SUCCESS)
5439 return r;
5441 return ERROR_SUCCESS;
5444 static void remove_product_upgrade_code( MSIPACKAGE *package )
5446 WCHAR *code, product[SQUASHED_GUID_SIZE];
5447 HKEY hkey;
5448 LONG res;
5449 DWORD count;
5451 squash_guid( package->ProductCode, product );
5452 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5454 WARN( "upgrade code not found\n" );
5455 return;
5457 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5459 RegDeleteValueW( hkey, product );
5460 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5461 RegCloseKey( hkey );
5462 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5464 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5466 RegDeleteValueW( hkey, product );
5467 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5468 RegCloseKey( hkey );
5469 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5471 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5473 RegDeleteValueW( hkey, product );
5474 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5475 RegCloseKey( hkey );
5476 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5479 msi_free( code );
5482 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5484 MSIPATCHINFO *patch;
5486 MSIREG_DeleteProductKey(package->ProductCode);
5487 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5488 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5490 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5491 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5492 MSIREG_DeleteUserProductKey(package->ProductCode);
5493 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5495 remove_product_upgrade_code( package );
5497 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5499 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5500 if (!strcmpW( package->ProductCode, patch->products ))
5502 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5503 patch->delete_on_close = TRUE;
5505 /* FIXME: remove local patch package if this is the last product */
5507 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5508 package->delete_on_close = TRUE;
5510 msi_unpublish_icons( package );
5511 return ERROR_SUCCESS;
5514 static BOOL is_full_uninstall( MSIPACKAGE *package )
5516 MSIFEATURE *feature;
5518 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5520 if (feature->Action != INSTALLSTATE_ABSENT &&
5521 (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
5522 return FALSE;
5525 return TRUE;
5528 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5530 UINT rc;
5532 /* first do the same as an InstallExecute */
5533 rc = execute_script(package, SCRIPT_INSTALL);
5534 if (rc != ERROR_SUCCESS)
5535 return rc;
5537 /* then handle commit actions */
5538 rc = execute_script(package, SCRIPT_COMMIT);
5539 if (rc != ERROR_SUCCESS)
5540 return rc;
5542 if (is_full_uninstall(package))
5543 rc = ACTION_UnpublishProduct(package);
5545 return rc;
5548 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5550 static const WCHAR RunOnce[] = {
5551 'S','o','f','t','w','a','r','e','\\',
5552 'M','i','c','r','o','s','o','f','t','\\',
5553 'W','i','n','d','o','w','s','\\',
5554 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5555 'R','u','n','O','n','c','e',0};
5556 static const WCHAR InstallRunOnce[] = {
5557 'S','o','f','t','w','a','r','e','\\',
5558 'M','i','c','r','o','s','o','f','t','\\',
5559 'W','i','n','d','o','w','s','\\',
5560 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5561 'I','n','s','t','a','l','l','e','r','\\',
5562 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5564 static const WCHAR msiexec_fmt[] = {
5565 '%','s',
5566 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5567 '\"','%','s','\"',0};
5568 static const WCHAR install_fmt[] = {
5569 '/','I',' ','\"','%','s','\"',' ',
5570 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5571 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5572 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5573 HKEY hkey;
5575 squash_guid( package->ProductCode, squashed_pc );
5577 GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir));
5578 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5579 snprintfW(buffer, ARRAY_SIZE(buffer), msiexec_fmt, sysdir, squashed_pc);
5581 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5582 RegCloseKey(hkey);
5584 TRACE("Reboot command %s\n",debugstr_w(buffer));
5586 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5587 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5589 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5590 RegCloseKey(hkey);
5592 return ERROR_INSTALL_SUSPEND;
5595 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5597 DWORD attrib;
5598 UINT rc;
5601 * We are currently doing what should be done here in the top level Install
5602 * however for Administrative and uninstalls this step will be needed
5604 if (!package->PackagePath)
5605 return ERROR_SUCCESS;
5607 msi_set_sourcedir_props(package, TRUE);
5609 attrib = GetFileAttributesW(package->db->path);
5610 if (attrib == INVALID_FILE_ATTRIBUTES)
5612 MSIRECORD *record;
5613 LPWSTR prompt;
5614 DWORD size = 0;
5616 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5617 package->Context, MSICODE_PRODUCT,
5618 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5619 if (rc == ERROR_MORE_DATA)
5621 prompt = msi_alloc(size * sizeof(WCHAR));
5622 MsiSourceListGetInfoW(package->ProductCode, NULL,
5623 package->Context, MSICODE_PRODUCT,
5624 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5626 else
5627 prompt = strdupW(package->db->path);
5629 record = MSI_CreateRecord(2);
5630 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5631 MSI_RecordSetStringW(record, 2, prompt);
5632 msi_free(prompt);
5633 while(attrib == INVALID_FILE_ATTRIBUTES)
5635 MSI_RecordSetStringW(record, 0, NULL);
5636 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5637 if (rc == IDCANCEL)
5638 return ERROR_INSTALL_USEREXIT;
5639 attrib = GetFileAttributesW(package->db->path);
5641 rc = ERROR_SUCCESS;
5643 else
5644 return ERROR_SUCCESS;
5646 return rc;
5649 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5651 HKEY hkey = 0;
5652 LPWSTR buffer, productid = NULL;
5653 UINT i, rc = ERROR_SUCCESS;
5654 MSIRECORD *uirow;
5656 static const WCHAR szPropKeys[][80] =
5658 {'P','r','o','d','u','c','t','I','D',0},
5659 {'U','S','E','R','N','A','M','E',0},
5660 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5661 {0},
5664 static const WCHAR szRegKeys[][80] =
5666 {'P','r','o','d','u','c','t','I','D',0},
5667 {'R','e','g','O','w','n','e','r',0},
5668 {'R','e','g','C','o','m','p','a','n','y',0},
5669 {0},
5672 if (package->script == SCRIPT_NONE)
5673 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterUser);
5675 if (msi_check_unpublish(package))
5677 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5678 goto end;
5681 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5682 if (!productid)
5683 goto end;
5685 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5686 NULL, &hkey, TRUE);
5687 if (rc != ERROR_SUCCESS)
5688 goto end;
5690 for( i = 0; szPropKeys[i][0]; i++ )
5692 buffer = msi_dup_property( package->db, szPropKeys[i] );
5693 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5694 msi_free( buffer );
5697 end:
5698 uirow = MSI_CreateRecord( 1 );
5699 MSI_RecordSetStringW( uirow, 1, productid );
5700 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5701 msiobj_release( &uirow->hdr );
5703 msi_free(productid);
5704 RegCloseKey(hkey);
5705 return rc;
5708 static UINT iterate_properties(MSIRECORD *record, void *param)
5710 static const WCHAR prop_template[] =
5711 {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0};
5712 MSIRECORD *uirow;
5714 uirow = MSI_CloneRecord(record);
5715 if (!uirow) return ERROR_OUTOFMEMORY;
5716 MSI_RecordSetStringW(uirow, 0, prop_template);
5717 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5718 msiobj_release(&uirow->hdr);
5720 return ERROR_SUCCESS;
5724 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5726 static const WCHAR prop_query[] =
5727 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0};
5728 WCHAR *productname;
5729 WCHAR *action;
5730 WCHAR *info_template;
5731 MSIQUERY *view;
5732 MSIRECORD *uirow, *uirow_info;
5733 UINT rc;
5735 /* Send COMMONDATA and INFO messages. */
5736 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5737 uirow = MSI_CreateRecord(3);
5738 if (!uirow) return ERROR_OUTOFMEMORY;
5739 MSI_RecordSetStringW(uirow, 0, NULL);
5740 MSI_RecordSetInteger(uirow, 1, 0);
5741 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5742 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5743 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5744 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5745 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5747 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5749 uirow_info = MSI_CreateRecord(0);
5750 if (!uirow_info)
5752 msiobj_release(&uirow->hdr);
5753 return ERROR_OUTOFMEMORY;
5755 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5756 MSI_RecordSetStringW(uirow_info, 0, info_template);
5757 msi_free(info_template);
5758 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5759 msiobj_release(&uirow_info->hdr);
5762 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5764 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5765 MSI_RecordSetInteger(uirow, 1, 1);
5766 MSI_RecordSetStringW(uirow, 2, productname);
5767 MSI_RecordSetStringW(uirow, 3, NULL);
5768 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5769 msiobj_release(&uirow->hdr);
5771 package->LastActionResult = MSI_NULL_INTEGER;
5773 action = msi_dup_property(package->db, szEXECUTEACTION);
5774 if (!action) action = msi_strdupW(szINSTALL, strlenW(szINSTALL));
5776 /* Perform the action. Top-level actions trigger a sequence. */
5777 if (!strcmpW(action, szINSTALL))
5779 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5780 ui_actionstart(package, szINSTALL, NULL, NULL);
5781 ui_actioninfo(package, szINSTALL, TRUE, 0);
5782 uirow = MSI_CreateRecord(2);
5783 if (!uirow)
5785 rc = ERROR_OUTOFMEMORY;
5786 goto end;
5788 MSI_RecordSetStringW(uirow, 0, NULL);
5789 MSI_RecordSetStringW(uirow, 1, productname);
5790 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5791 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5792 msiobj_release(&uirow->hdr);
5794 /* Perform the installation. Always use the ExecuteSequence. */
5795 package->InWhatSequence |= SEQUENCE_EXEC;
5796 rc = ACTION_ProcessExecSequence(package);
5798 /* Send return value and INSTALLEND. */
5799 ui_actioninfo(package, szINSTALL, FALSE, !rc);
5800 uirow = MSI_CreateRecord(3);
5801 if (!uirow)
5803 rc = ERROR_OUTOFMEMORY;
5804 goto end;
5806 MSI_RecordSetStringW(uirow, 0, NULL);
5807 MSI_RecordSetStringW(uirow, 1, productname);
5808 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5809 MSI_RecordSetInteger(uirow, 3, !rc);
5810 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5811 msiobj_release(&uirow->hdr);
5813 else
5814 rc = ACTION_PerformAction(package, action);
5816 /* Send all set properties. */
5817 if (!MSI_OpenQuery(package->db, &view, prop_query))
5819 MSI_IterateRecords(view, NULL, iterate_properties, package);
5820 msiobj_release(&view->hdr);
5823 /* And finally, toggle the cancel off and on. */
5824 uirow = MSI_CreateRecord(2);
5825 if (!uirow)
5827 rc = ERROR_OUTOFMEMORY;
5828 goto end;
5830 MSI_RecordSetStringW(uirow, 0, NULL);
5831 MSI_RecordSetInteger(uirow, 1, 2);
5832 MSI_RecordSetInteger(uirow, 2, 0);
5833 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5834 MSI_RecordSetInteger(uirow, 2, 1);
5835 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5836 msiobj_release(&uirow->hdr);
5838 end:
5839 msi_free(productname);
5840 msi_free(action);
5841 return rc;
5844 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5846 msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1);
5847 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5849 package->InWhatSequence |= SEQUENCE_UI;
5850 return ACTION_ProcessUISequence(package);
5852 else
5853 return ACTION_ExecuteAction(package);
5856 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5858 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5859 WCHAR productid_85[21], component_85[21], *ret;
5860 GUID clsid;
5861 DWORD sz;
5863 /* > is used if there is a component GUID and < if not. */
5865 productid_85[0] = 0;
5866 component_85[0] = 0;
5867 CLSIDFromString( package->ProductCode, &clsid );
5869 encode_base85_guid( &clsid, productid_85 );
5870 if (component)
5872 CLSIDFromString( component->ComponentId, &clsid );
5873 encode_base85_guid( &clsid, component_85 );
5876 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5877 debugstr_w(component_85));
5879 sz = 20 + strlenW( feature ) + 20 + 3;
5880 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5881 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5882 return ret;
5885 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5887 MSIPACKAGE *package = param;
5888 LPCWSTR compgroupid, component, feature, qualifier, text;
5889 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5890 HKEY hkey = NULL;
5891 UINT rc;
5892 MSICOMPONENT *comp;
5893 MSIFEATURE *feat;
5894 DWORD sz;
5895 MSIRECORD *uirow;
5896 int len;
5898 feature = MSI_RecordGetString(rec, 5);
5899 feat = msi_get_loaded_feature(package, feature);
5900 if (!feat)
5901 return ERROR_SUCCESS;
5903 feat->Action = msi_get_feature_action( package, feat );
5904 if (feat->Action != INSTALLSTATE_LOCAL &&
5905 feat->Action != INSTALLSTATE_SOURCE &&
5906 feat->Action != INSTALLSTATE_ADVERTISED)
5908 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5909 return ERROR_SUCCESS;
5912 component = MSI_RecordGetString(rec, 3);
5913 comp = msi_get_loaded_component(package, component);
5914 if (!comp)
5915 return ERROR_SUCCESS;
5917 compgroupid = MSI_RecordGetString(rec,1);
5918 qualifier = MSI_RecordGetString(rec,2);
5920 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5921 if (rc != ERROR_SUCCESS)
5922 goto end;
5924 advertise = msi_create_component_advertise_string( package, comp, feature );
5925 text = MSI_RecordGetString( rec, 4 );
5926 if (text)
5928 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5929 strcpyW( p, advertise );
5930 strcatW( p, text );
5931 msi_free( advertise );
5932 advertise = p;
5934 existing = msi_reg_get_val_str( hkey, qualifier );
5936 sz = strlenW( advertise ) + 1;
5937 if (existing)
5939 for (p = existing; *p; p += len)
5941 len = strlenW( p ) + 1;
5942 if (strcmpW( advertise, p )) sz += len;
5945 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5947 rc = ERROR_OUTOFMEMORY;
5948 goto end;
5950 q = output;
5951 if (existing)
5953 for (p = existing; *p; p += len)
5955 len = strlenW( p ) + 1;
5956 if (strcmpW( advertise, p ))
5958 memcpy( q, p, len * sizeof(WCHAR) );
5959 q += len;
5963 strcpyW( q, advertise );
5964 q[strlenW( q ) + 1] = 0;
5966 msi_reg_set_val_multi_str( hkey, qualifier, output );
5968 end:
5969 RegCloseKey(hkey);
5970 msi_free( output );
5971 msi_free( advertise );
5972 msi_free( existing );
5974 /* the UI chunk */
5975 uirow = MSI_CreateRecord( 2 );
5976 MSI_RecordSetStringW( uirow, 1, compgroupid );
5977 MSI_RecordSetStringW( uirow, 2, qualifier);
5978 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5979 msiobj_release( &uirow->hdr );
5980 /* FIXME: call ui_progress? */
5982 return rc;
5986 * At present I am ignorning the advertised components part of this and only
5987 * focusing on the qualified component sets
5989 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5991 static const WCHAR query[] = {
5992 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5993 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5994 MSIQUERY *view;
5995 UINT rc;
5997 if (package->script == SCRIPT_NONE)
5998 return msi_schedule_action(package, SCRIPT_INSTALL, szPublishComponents);
6000 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6001 if (rc != ERROR_SUCCESS)
6002 return ERROR_SUCCESS;
6004 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
6005 msiobj_release(&view->hdr);
6006 return rc;
6009 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
6011 static const WCHAR szInstallerComponents[] = {
6012 'S','o','f','t','w','a','r','e','\\',
6013 'M','i','c','r','o','s','o','f','t','\\',
6014 'I','n','s','t','a','l','l','e','r','\\',
6015 'C','o','m','p','o','n','e','n','t','s','\\',0};
6017 MSIPACKAGE *package = param;
6018 LPCWSTR compgroupid, component, feature, qualifier;
6019 MSICOMPONENT *comp;
6020 MSIFEATURE *feat;
6021 MSIRECORD *uirow;
6022 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
6023 LONG res;
6025 feature = MSI_RecordGetString( rec, 5 );
6026 feat = msi_get_loaded_feature( package, feature );
6027 if (!feat)
6028 return ERROR_SUCCESS;
6030 feat->Action = msi_get_feature_action( package, feat );
6031 if (feat->Action != INSTALLSTATE_ABSENT)
6033 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
6034 return ERROR_SUCCESS;
6037 component = MSI_RecordGetString( rec, 3 );
6038 comp = msi_get_loaded_component( package, component );
6039 if (!comp)
6040 return ERROR_SUCCESS;
6042 compgroupid = MSI_RecordGetString( rec, 1 );
6043 qualifier = MSI_RecordGetString( rec, 2 );
6045 squash_guid( compgroupid, squashed );
6046 strcpyW( keypath, szInstallerComponents );
6047 strcatW( keypath, squashed );
6049 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
6050 if (res != ERROR_SUCCESS)
6052 WARN("Unable to delete component key %d\n", res);
6055 uirow = MSI_CreateRecord( 2 );
6056 MSI_RecordSetStringW( uirow, 1, compgroupid );
6057 MSI_RecordSetStringW( uirow, 2, qualifier );
6058 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6059 msiobj_release( &uirow->hdr );
6061 return ERROR_SUCCESS;
6064 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6066 static const WCHAR query[] = {
6067 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6068 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
6069 MSIQUERY *view;
6070 UINT rc;
6072 if (package->script == SCRIPT_NONE)
6073 return msi_schedule_action(package, SCRIPT_INSTALL, szUnpublishComponents);
6075 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6076 if (rc != ERROR_SUCCESS)
6077 return ERROR_SUCCESS;
6079 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
6080 msiobj_release( &view->hdr );
6081 return rc;
6084 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
6086 static const WCHAR query[] =
6087 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6088 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
6089 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
6090 MSIPACKAGE *package = param;
6091 MSICOMPONENT *component;
6092 MSIRECORD *row;
6093 MSIFILE *file;
6094 SC_HANDLE hscm = NULL, service = NULL;
6095 LPCWSTR comp, key;
6096 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
6097 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
6098 DWORD serv_type, start_type, err_control;
6099 BOOL is_vital;
6100 SERVICE_DESCRIPTIONW sd = {NULL};
6101 UINT ret = ERROR_SUCCESS;
6103 comp = MSI_RecordGetString( rec, 12 );
6104 component = msi_get_loaded_component( package, comp );
6105 if (!component)
6107 WARN("service component not found\n");
6108 goto done;
6110 component->Action = msi_get_component_action( package, component );
6111 if (component->Action != INSTALLSTATE_LOCAL)
6113 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
6114 goto done;
6116 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
6117 if (!hscm)
6119 ERR("Failed to open the SC Manager!\n");
6120 goto done;
6123 start_type = MSI_RecordGetInteger(rec, 5);
6124 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
6125 goto done;
6127 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
6128 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
6129 serv_type = MSI_RecordGetInteger(rec, 4);
6130 err_control = MSI_RecordGetInteger(rec, 6);
6131 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
6132 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
6133 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
6134 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
6135 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
6136 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
6138 /* Should the complete install fail if CreateService fails? */
6139 is_vital = (err_control & msidbServiceInstallErrorControlVital);
6141 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
6142 CreateService (under Windows) would fail if not. */
6143 err_control &= ~msidbServiceInstallErrorControlVital;
6145 /* fetch the service path */
6146 row = MSI_QueryGetRecord(package->db, query, comp);
6147 if (!row)
6149 ERR("Query failed\n");
6150 goto done;
6152 if (!(key = MSI_RecordGetString(row, 6)))
6154 msiobj_release(&row->hdr);
6155 goto done;
6157 file = msi_get_loaded_file(package, key);
6158 msiobj_release(&row->hdr);
6159 if (!file)
6161 ERR("Failed to load the service file\n");
6162 goto done;
6165 if (!args || !args[0]) image_path = file->TargetPath;
6166 else
6168 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
6169 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6171 ret = ERROR_OUTOFMEMORY;
6172 goto done;
6175 strcpyW(image_path, file->TargetPath);
6176 strcatW(image_path, szSpace);
6177 strcatW(image_path, args);
6179 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6180 start_type, err_control, image_path, load_order,
6181 NULL, depends, serv_name, pass);
6183 if (!service)
6185 if (GetLastError() != ERROR_SERVICE_EXISTS)
6187 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6188 if (is_vital)
6189 ret = ERROR_INSTALL_FAILURE;
6193 else if (sd.lpDescription)
6195 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6196 WARN("failed to set service description %u\n", GetLastError());
6199 if (image_path != file->TargetPath) msi_free(image_path);
6200 done:
6201 if (service) CloseServiceHandle(service);
6202 if (hscm) CloseServiceHandle(hscm);
6203 msi_free(name);
6204 msi_free(disp);
6205 msi_free(sd.lpDescription);
6206 msi_free(load_order);
6207 msi_free(serv_name);
6208 msi_free(pass);
6209 msi_free(depends);
6210 msi_free(args);
6212 return ret;
6215 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6217 static const WCHAR query[] = {
6218 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6219 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6220 MSIQUERY *view;
6221 UINT rc;
6223 if (package->script == SCRIPT_NONE)
6224 return msi_schedule_action(package, SCRIPT_INSTALL, szInstallServices);
6226 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6227 if (rc != ERROR_SUCCESS)
6228 return ERROR_SUCCESS;
6230 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6231 msiobj_release(&view->hdr);
6232 return rc;
6235 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6236 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6238 LPCWSTR *vector, *temp_vector;
6239 LPWSTR p, q;
6240 DWORD sep_len;
6242 static const WCHAR separator[] = {'[','~',']',0};
6244 *numargs = 0;
6245 sep_len = ARRAY_SIZE(separator) - 1;
6247 if (!args)
6248 return NULL;
6250 vector = msi_alloc(sizeof(LPWSTR));
6251 if (!vector)
6252 return NULL;
6254 p = args;
6257 (*numargs)++;
6258 vector[*numargs - 1] = p;
6260 if ((q = strstrW(p, separator)))
6262 *q = '\0';
6264 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6265 if (!temp_vector)
6267 msi_free(vector);
6268 return NULL;
6270 vector = temp_vector;
6272 p = q + sep_len;
6274 } while (q);
6276 return vector;
6279 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6281 MSIPACKAGE *package = param;
6282 MSICOMPONENT *comp;
6283 MSIRECORD *uirow;
6284 SC_HANDLE scm = NULL, service = NULL;
6285 LPCWSTR component, *vector = NULL;
6286 LPWSTR name, args, display_name = NULL;
6287 DWORD event, numargs, len, wait, dummy;
6288 UINT r = ERROR_FUNCTION_FAILED;
6289 SERVICE_STATUS_PROCESS status;
6290 ULONGLONG start_time;
6292 component = MSI_RecordGetString(rec, 6);
6293 comp = msi_get_loaded_component(package, component);
6294 if (!comp)
6295 return ERROR_SUCCESS;
6297 event = MSI_RecordGetInteger( rec, 3 );
6298 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6300 comp->Action = msi_get_component_action( package, comp );
6301 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6302 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6304 TRACE("not starting %s\n", debugstr_w(name));
6305 msi_free( name );
6306 return ERROR_SUCCESS;
6309 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6310 wait = MSI_RecordGetInteger(rec, 5);
6312 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6313 if (!scm)
6315 ERR("Failed to open the service control manager\n");
6316 goto done;
6319 len = 0;
6320 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6321 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6323 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6324 GetServiceDisplayNameW( scm, name, display_name, &len );
6327 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6328 if (!service)
6330 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6331 goto done;
6334 vector = msi_service_args_to_vector(args, &numargs);
6336 if (!StartServiceW(service, numargs, vector) &&
6337 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6339 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6340 goto done;
6343 r = ERROR_SUCCESS;
6344 if (wait)
6346 /* wait for at most 30 seconds for the service to be up and running */
6347 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6348 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6350 TRACE("failed to query service status (%u)\n", GetLastError());
6351 goto done;
6353 start_time = GetTickCount64();
6354 while (status.dwCurrentState == SERVICE_START_PENDING)
6356 if (GetTickCount64() - start_time > 30000) break;
6357 Sleep(1000);
6358 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6359 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6361 TRACE("failed to query service status (%u)\n", GetLastError());
6362 goto done;
6365 if (status.dwCurrentState != SERVICE_RUNNING)
6367 WARN("service failed to start %u\n", status.dwCurrentState);
6368 r = ERROR_FUNCTION_FAILED;
6372 done:
6373 uirow = MSI_CreateRecord( 2 );
6374 MSI_RecordSetStringW( uirow, 1, display_name );
6375 MSI_RecordSetStringW( uirow, 2, name );
6376 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6377 msiobj_release( &uirow->hdr );
6379 if (service) CloseServiceHandle(service);
6380 if (scm) CloseServiceHandle(scm);
6382 msi_free(name);
6383 msi_free(args);
6384 msi_free(vector);
6385 msi_free(display_name);
6386 return r;
6389 static UINT ACTION_StartServices( MSIPACKAGE *package )
6391 static const WCHAR query[] = {
6392 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6393 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6394 MSIQUERY *view;
6395 UINT rc;
6397 if (package->script == SCRIPT_NONE)
6398 return msi_schedule_action(package, SCRIPT_INSTALL, szStartServices);
6400 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6401 if (rc != ERROR_SUCCESS)
6402 return ERROR_SUCCESS;
6404 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6405 msiobj_release(&view->hdr);
6406 return rc;
6409 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6411 DWORD i, needed, count;
6412 ENUM_SERVICE_STATUSW *dependencies;
6413 SERVICE_STATUS ss;
6414 SC_HANDLE depserv;
6415 BOOL stopped, ret = FALSE;
6417 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6418 0, &needed, &count))
6419 return TRUE;
6421 if (GetLastError() != ERROR_MORE_DATA)
6422 return FALSE;
6424 dependencies = msi_alloc(needed);
6425 if (!dependencies)
6426 return FALSE;
6428 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6429 needed, &needed, &count))
6430 goto done;
6432 for (i = 0; i < count; i++)
6434 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6435 SERVICE_STOP | SERVICE_QUERY_STATUS);
6436 if (!depserv)
6437 goto done;
6439 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6440 CloseServiceHandle(depserv);
6441 if (!stopped)
6442 goto done;
6445 ret = TRUE;
6447 done:
6448 msi_free(dependencies);
6449 return ret;
6452 static UINT stop_service( LPCWSTR name )
6454 SC_HANDLE scm = NULL, service = NULL;
6455 SERVICE_STATUS status;
6456 SERVICE_STATUS_PROCESS ssp;
6457 DWORD needed;
6459 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6460 if (!scm)
6462 WARN("Failed to open the SCM: %d\n", GetLastError());
6463 goto done;
6466 service = OpenServiceW(scm, name,
6467 SERVICE_STOP |
6468 SERVICE_QUERY_STATUS |
6469 SERVICE_ENUMERATE_DEPENDENTS);
6470 if (!service)
6472 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6473 goto done;
6476 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6477 sizeof(SERVICE_STATUS_PROCESS), &needed))
6479 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6480 goto done;
6483 if (ssp.dwCurrentState == SERVICE_STOPPED)
6484 goto done;
6486 stop_service_dependents(scm, service);
6488 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6489 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6491 done:
6492 if (service) CloseServiceHandle(service);
6493 if (scm) CloseServiceHandle(scm);
6495 return ERROR_SUCCESS;
6498 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6500 MSIPACKAGE *package = param;
6501 MSICOMPONENT *comp;
6502 MSIRECORD *uirow;
6503 LPCWSTR component;
6504 WCHAR *name, *display_name = NULL;
6505 DWORD event, len;
6506 SC_HANDLE scm;
6508 component = MSI_RecordGetString( rec, 6 );
6509 comp = msi_get_loaded_component( package, component );
6510 if (!comp)
6511 return ERROR_SUCCESS;
6513 event = MSI_RecordGetInteger( rec, 3 );
6514 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6516 comp->Action = msi_get_component_action( package, comp );
6517 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6518 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6520 TRACE("not stopping %s\n", debugstr_w(name));
6521 msi_free( name );
6522 return ERROR_SUCCESS;
6525 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6526 if (!scm)
6528 ERR("Failed to open the service control manager\n");
6529 goto done;
6532 len = 0;
6533 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6534 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6536 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6537 GetServiceDisplayNameW( scm, name, display_name, &len );
6539 CloseServiceHandle( scm );
6541 stop_service( name );
6543 done:
6544 uirow = MSI_CreateRecord( 2 );
6545 MSI_RecordSetStringW( uirow, 1, display_name );
6546 MSI_RecordSetStringW( uirow, 2, name );
6547 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6548 msiobj_release( &uirow->hdr );
6550 msi_free( name );
6551 msi_free( display_name );
6552 return ERROR_SUCCESS;
6555 static UINT ACTION_StopServices( MSIPACKAGE *package )
6557 static const WCHAR query[] = {
6558 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6559 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6560 MSIQUERY *view;
6561 UINT rc;
6563 if (package->script == SCRIPT_NONE)
6564 return msi_schedule_action(package, SCRIPT_INSTALL, szStopServices);
6566 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6567 if (rc != ERROR_SUCCESS)
6568 return ERROR_SUCCESS;
6570 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6571 msiobj_release(&view->hdr);
6572 return rc;
6575 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6577 MSIPACKAGE *package = param;
6578 MSICOMPONENT *comp;
6579 MSIRECORD *uirow;
6580 LPWSTR name = NULL, display_name = NULL;
6581 DWORD event, len;
6582 SC_HANDLE scm = NULL, service = NULL;
6584 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6585 if (!comp)
6586 return ERROR_SUCCESS;
6588 event = MSI_RecordGetInteger( rec, 3 );
6589 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6591 comp->Action = msi_get_component_action( package, comp );
6592 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6593 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6595 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6596 msi_free( name );
6597 return ERROR_SUCCESS;
6599 stop_service( name );
6601 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6602 if (!scm)
6604 WARN("Failed to open the SCM: %d\n", GetLastError());
6605 goto done;
6608 len = 0;
6609 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6610 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6612 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6613 GetServiceDisplayNameW( scm, name, display_name, &len );
6616 service = OpenServiceW( scm, name, DELETE );
6617 if (!service)
6619 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6620 goto done;
6623 if (!DeleteService( service ))
6624 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6626 done:
6627 uirow = MSI_CreateRecord( 2 );
6628 MSI_RecordSetStringW( uirow, 1, display_name );
6629 MSI_RecordSetStringW( uirow, 2, name );
6630 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6631 msiobj_release( &uirow->hdr );
6633 if (service) CloseServiceHandle( service );
6634 if (scm) CloseServiceHandle( scm );
6635 msi_free( name );
6636 msi_free( display_name );
6638 return ERROR_SUCCESS;
6641 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6643 static const WCHAR query[] = {
6644 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6645 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6646 MSIQUERY *view;
6647 UINT rc;
6649 if (package->script == SCRIPT_NONE)
6650 return msi_schedule_action(package, SCRIPT_INSTALL, szDeleteServices);
6652 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6653 if (rc != ERROR_SUCCESS)
6654 return ERROR_SUCCESS;
6656 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6657 msiobj_release( &view->hdr );
6658 return rc;
6661 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6663 MSIPACKAGE *package = param;
6664 LPWSTR driver, driver_path, ptr;
6665 WCHAR outpath[MAX_PATH];
6666 MSIFILE *driver_file = NULL, *setup_file = NULL;
6667 MSICOMPONENT *comp;
6668 MSIRECORD *uirow;
6669 LPCWSTR desc, file_key, component;
6670 DWORD len, usage;
6671 UINT r = ERROR_SUCCESS;
6673 static const WCHAR driver_fmt[] = {
6674 'D','r','i','v','e','r','=','%','s',0};
6675 static const WCHAR setup_fmt[] = {
6676 'S','e','t','u','p','=','%','s',0};
6677 static const WCHAR usage_fmt[] = {
6678 'F','i','l','e','U','s','a','g','e','=','1',0};
6680 component = MSI_RecordGetString( rec, 2 );
6681 comp = msi_get_loaded_component( package, component );
6682 if (!comp)
6683 return ERROR_SUCCESS;
6685 comp->Action = msi_get_component_action( package, comp );
6686 if (comp->Action != INSTALLSTATE_LOCAL)
6688 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6689 return ERROR_SUCCESS;
6691 desc = MSI_RecordGetString(rec, 3);
6693 file_key = MSI_RecordGetString( rec, 4 );
6694 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6696 file_key = MSI_RecordGetString( rec, 5 );
6697 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6699 if (!driver_file)
6701 ERR("ODBC Driver entry not found!\n");
6702 return ERROR_FUNCTION_FAILED;
6705 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6706 if (setup_file)
6707 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6708 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6710 driver = msi_alloc(len * sizeof(WCHAR));
6711 if (!driver)
6712 return ERROR_OUTOFMEMORY;
6714 ptr = driver;
6715 lstrcpyW(ptr, desc);
6716 ptr += lstrlenW(ptr) + 1;
6718 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6719 ptr += len + 1;
6721 if (setup_file)
6723 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6724 ptr += len + 1;
6727 lstrcpyW(ptr, usage_fmt);
6728 ptr += lstrlenW(ptr) + 1;
6729 *ptr = '\0';
6731 if (!driver_file->TargetPath)
6733 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6734 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6736 driver_path = strdupW(driver_file->TargetPath);
6737 ptr = strrchrW(driver_path, '\\');
6738 if (ptr) *ptr = '\0';
6740 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6741 NULL, ODBC_INSTALL_COMPLETE, &usage))
6743 ERR("Failed to install SQL driver!\n");
6744 r = ERROR_FUNCTION_FAILED;
6747 uirow = MSI_CreateRecord( 5 );
6748 MSI_RecordSetStringW( uirow, 1, desc );
6749 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6750 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6751 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6752 msiobj_release( &uirow->hdr );
6754 msi_free(driver);
6755 msi_free(driver_path);
6757 return r;
6760 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6762 MSIPACKAGE *package = param;
6763 LPWSTR translator, translator_path, ptr;
6764 WCHAR outpath[MAX_PATH];
6765 MSIFILE *translator_file = NULL, *setup_file = NULL;
6766 MSICOMPONENT *comp;
6767 MSIRECORD *uirow;
6768 LPCWSTR desc, file_key, component;
6769 DWORD len, usage;
6770 UINT r = ERROR_SUCCESS;
6772 static const WCHAR translator_fmt[] = {
6773 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6774 static const WCHAR setup_fmt[] = {
6775 'S','e','t','u','p','=','%','s',0};
6777 component = MSI_RecordGetString( rec, 2 );
6778 comp = msi_get_loaded_component( package, component );
6779 if (!comp)
6780 return ERROR_SUCCESS;
6782 comp->Action = msi_get_component_action( package, comp );
6783 if (comp->Action != INSTALLSTATE_LOCAL)
6785 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6786 return ERROR_SUCCESS;
6788 desc = MSI_RecordGetString(rec, 3);
6790 file_key = MSI_RecordGetString( rec, 4 );
6791 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6793 file_key = MSI_RecordGetString( rec, 5 );
6794 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6796 if (!translator_file)
6798 ERR("ODBC Translator entry not found!\n");
6799 return ERROR_FUNCTION_FAILED;
6802 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6803 if (setup_file)
6804 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6806 translator = msi_alloc(len * sizeof(WCHAR));
6807 if (!translator)
6808 return ERROR_OUTOFMEMORY;
6810 ptr = translator;
6811 lstrcpyW(ptr, desc);
6812 ptr += lstrlenW(ptr) + 1;
6814 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6815 ptr += len + 1;
6817 if (setup_file)
6819 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6820 ptr += len + 1;
6822 *ptr = '\0';
6824 translator_path = strdupW(translator_file->TargetPath);
6825 ptr = strrchrW(translator_path, '\\');
6826 if (ptr) *ptr = '\0';
6828 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6829 NULL, ODBC_INSTALL_COMPLETE, &usage))
6831 ERR("Failed to install SQL translator!\n");
6832 r = ERROR_FUNCTION_FAILED;
6835 uirow = MSI_CreateRecord( 5 );
6836 MSI_RecordSetStringW( uirow, 1, desc );
6837 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6838 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6839 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6840 msiobj_release( &uirow->hdr );
6842 msi_free(translator);
6843 msi_free(translator_path);
6845 return r;
6848 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6850 MSIPACKAGE *package = param;
6851 MSICOMPONENT *comp;
6852 LPWSTR attrs;
6853 LPCWSTR desc, driver, component;
6854 WORD request = ODBC_ADD_SYS_DSN;
6855 INT registration;
6856 DWORD len;
6857 UINT r = ERROR_SUCCESS;
6858 MSIRECORD *uirow;
6860 static const WCHAR attrs_fmt[] = {
6861 'D','S','N','=','%','s',0 };
6863 component = MSI_RecordGetString( rec, 2 );
6864 comp = msi_get_loaded_component( package, component );
6865 if (!comp)
6866 return ERROR_SUCCESS;
6868 comp->Action = msi_get_component_action( package, comp );
6869 if (comp->Action != INSTALLSTATE_LOCAL)
6871 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6872 return ERROR_SUCCESS;
6875 desc = MSI_RecordGetString(rec, 3);
6876 driver = MSI_RecordGetString(rec, 4);
6877 registration = MSI_RecordGetInteger(rec, 5);
6879 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6880 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6882 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6883 attrs = msi_alloc(len * sizeof(WCHAR));
6884 if (!attrs)
6885 return ERROR_OUTOFMEMORY;
6887 len = sprintfW(attrs, attrs_fmt, desc);
6888 attrs[len + 1] = 0;
6890 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6892 ERR("Failed to install SQL data source!\n");
6893 r = ERROR_FUNCTION_FAILED;
6896 uirow = MSI_CreateRecord( 5 );
6897 MSI_RecordSetStringW( uirow, 1, desc );
6898 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6899 MSI_RecordSetInteger( uirow, 3, request );
6900 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6901 msiobj_release( &uirow->hdr );
6903 msi_free(attrs);
6905 return r;
6908 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6910 static const WCHAR driver_query[] = {
6911 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6912 'O','D','B','C','D','r','i','v','e','r',0};
6913 static const WCHAR translator_query[] = {
6914 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6915 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6916 static const WCHAR source_query[] = {
6917 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6918 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6919 MSIQUERY *view;
6920 UINT rc;
6922 if (package->script == SCRIPT_NONE)
6923 return msi_schedule_action(package, SCRIPT_INSTALL, szInstallODBC);
6925 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6926 if (rc == ERROR_SUCCESS)
6928 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6929 msiobj_release(&view->hdr);
6930 if (rc != ERROR_SUCCESS)
6931 return rc;
6933 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6934 if (rc == ERROR_SUCCESS)
6936 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6937 msiobj_release(&view->hdr);
6938 if (rc != ERROR_SUCCESS)
6939 return rc;
6941 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6942 if (rc == ERROR_SUCCESS)
6944 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6945 msiobj_release(&view->hdr);
6946 if (rc != ERROR_SUCCESS)
6947 return rc;
6949 return ERROR_SUCCESS;
6952 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6954 MSIPACKAGE *package = param;
6955 MSICOMPONENT *comp;
6956 MSIRECORD *uirow;
6957 DWORD usage;
6958 LPCWSTR desc, component;
6960 component = MSI_RecordGetString( rec, 2 );
6961 comp = msi_get_loaded_component( package, component );
6962 if (!comp)
6963 return ERROR_SUCCESS;
6965 comp->Action = msi_get_component_action( package, comp );
6966 if (comp->Action != INSTALLSTATE_ABSENT)
6968 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6969 return ERROR_SUCCESS;
6972 desc = MSI_RecordGetString( rec, 3 );
6973 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6975 WARN("Failed to remove ODBC driver\n");
6977 else if (!usage)
6979 FIXME("Usage count reached 0\n");
6982 uirow = MSI_CreateRecord( 2 );
6983 MSI_RecordSetStringW( uirow, 1, desc );
6984 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6985 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6986 msiobj_release( &uirow->hdr );
6988 return ERROR_SUCCESS;
6991 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6993 MSIPACKAGE *package = param;
6994 MSICOMPONENT *comp;
6995 MSIRECORD *uirow;
6996 DWORD usage;
6997 LPCWSTR desc, component;
6999 component = MSI_RecordGetString( rec, 2 );
7000 comp = msi_get_loaded_component( package, component );
7001 if (!comp)
7002 return ERROR_SUCCESS;
7004 comp->Action = msi_get_component_action( package, comp );
7005 if (comp->Action != INSTALLSTATE_ABSENT)
7007 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7008 return ERROR_SUCCESS;
7011 desc = MSI_RecordGetString( rec, 3 );
7012 if (!SQLRemoveTranslatorW( desc, &usage ))
7014 WARN("Failed to remove ODBC translator\n");
7016 else if (!usage)
7018 FIXME("Usage count reached 0\n");
7021 uirow = MSI_CreateRecord( 2 );
7022 MSI_RecordSetStringW( uirow, 1, desc );
7023 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7024 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7025 msiobj_release( &uirow->hdr );
7027 return ERROR_SUCCESS;
7030 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
7032 MSIPACKAGE *package = param;
7033 MSICOMPONENT *comp;
7034 MSIRECORD *uirow;
7035 LPWSTR attrs;
7036 LPCWSTR desc, driver, component;
7037 WORD request = ODBC_REMOVE_SYS_DSN;
7038 INT registration;
7039 DWORD len;
7041 static const WCHAR attrs_fmt[] = {
7042 'D','S','N','=','%','s',0 };
7044 component = MSI_RecordGetString( rec, 2 );
7045 comp = msi_get_loaded_component( package, component );
7046 if (!comp)
7047 return ERROR_SUCCESS;
7049 comp->Action = msi_get_component_action( package, comp );
7050 if (comp->Action != INSTALLSTATE_ABSENT)
7052 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7053 return ERROR_SUCCESS;
7056 desc = MSI_RecordGetString( rec, 3 );
7057 driver = MSI_RecordGetString( rec, 4 );
7058 registration = MSI_RecordGetInteger( rec, 5 );
7060 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
7061 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
7063 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
7064 attrs = msi_alloc( len * sizeof(WCHAR) );
7065 if (!attrs)
7066 return ERROR_OUTOFMEMORY;
7068 FIXME("Use ODBCSourceAttribute table\n");
7070 len = sprintfW( attrs, attrs_fmt, desc );
7071 attrs[len + 1] = 0;
7073 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
7075 WARN("Failed to remove ODBC data source\n");
7077 msi_free( attrs );
7079 uirow = MSI_CreateRecord( 3 );
7080 MSI_RecordSetStringW( uirow, 1, desc );
7081 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7082 MSI_RecordSetInteger( uirow, 3, request );
7083 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7084 msiobj_release( &uirow->hdr );
7086 return ERROR_SUCCESS;
7089 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
7091 static const WCHAR driver_query[] = {
7092 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7093 'O','D','B','C','D','r','i','v','e','r',0};
7094 static const WCHAR translator_query[] = {
7095 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7096 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7097 static const WCHAR source_query[] = {
7098 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7099 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
7100 MSIQUERY *view;
7101 UINT rc;
7103 if (package->script == SCRIPT_NONE)
7104 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveODBC);
7106 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7107 if (rc == ERROR_SUCCESS)
7109 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
7110 msiobj_release( &view->hdr );
7111 if (rc != ERROR_SUCCESS)
7112 return rc;
7114 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7115 if (rc == ERROR_SUCCESS)
7117 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
7118 msiobj_release( &view->hdr );
7119 if (rc != ERROR_SUCCESS)
7120 return rc;
7122 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
7123 if (rc == ERROR_SUCCESS)
7125 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
7126 msiobj_release( &view->hdr );
7127 if (rc != ERROR_SUCCESS)
7128 return rc;
7130 return ERROR_SUCCESS;
7133 #define ENV_ACT_SETALWAYS 0x1
7134 #define ENV_ACT_SETABSENT 0x2
7135 #define ENV_ACT_REMOVE 0x4
7136 #define ENV_ACT_REMOVEMATCH 0x8
7138 #define ENV_MOD_MACHINE 0x20000000
7139 #define ENV_MOD_APPEND 0x40000000
7140 #define ENV_MOD_PREFIX 0x80000000
7141 #define ENV_MOD_MASK 0xC0000000
7143 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
7145 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
7147 LPCWSTR cptr = *name;
7149 static const WCHAR prefix[] = {'[','~',']',0};
7150 static const int prefix_len = 3;
7152 *flags = 0;
7153 while (*cptr)
7155 if (*cptr == '=')
7156 *flags |= ENV_ACT_SETALWAYS;
7157 else if (*cptr == '+')
7158 *flags |= ENV_ACT_SETABSENT;
7159 else if (*cptr == '-')
7160 *flags |= ENV_ACT_REMOVE;
7161 else if (*cptr == '!')
7162 *flags |= ENV_ACT_REMOVEMATCH;
7163 else if (*cptr == '*')
7164 *flags |= ENV_MOD_MACHINE;
7165 else
7166 break;
7168 cptr++;
7169 (*name)++;
7172 if (!*cptr)
7174 ERR("Missing environment variable\n");
7175 return ERROR_FUNCTION_FAILED;
7178 if (*value)
7180 LPCWSTR ptr = *value;
7181 if (!strncmpW(ptr, prefix, prefix_len))
7183 if (ptr[prefix_len] == szSemiColon[0])
7185 *flags |= ENV_MOD_APPEND;
7186 *value += lstrlenW(prefix);
7188 else
7190 *value = NULL;
7193 else if (lstrlenW(*value) >= prefix_len)
7195 ptr += lstrlenW(ptr) - prefix_len;
7196 if (!strcmpW( ptr, prefix ))
7198 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7200 *flags |= ENV_MOD_PREFIX;
7201 /* the "[~]" will be removed by deformat_string */;
7203 else
7205 *value = NULL;
7211 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7212 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7213 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7214 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7216 ERR("Invalid flags: %08x\n", *flags);
7217 return ERROR_FUNCTION_FAILED;
7220 if (!*flags)
7221 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7223 return ERROR_SUCCESS;
7226 static UINT open_env_key( DWORD flags, HKEY *key )
7228 static const WCHAR user_env[] =
7229 {'E','n','v','i','r','o','n','m','e','n','t',0};
7230 static const WCHAR machine_env[] =
7231 {'S','y','s','t','e','m','\\',
7232 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7233 'C','o','n','t','r','o','l','\\',
7234 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7235 'E','n','v','i','r','o','n','m','e','n','t',0};
7236 const WCHAR *env;
7237 HKEY root;
7238 LONG res;
7240 if (flags & ENV_MOD_MACHINE)
7242 env = machine_env;
7243 root = HKEY_LOCAL_MACHINE;
7245 else
7247 env = user_env;
7248 root = HKEY_CURRENT_USER;
7251 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7252 if (res != ERROR_SUCCESS)
7254 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7255 return ERROR_FUNCTION_FAILED;
7258 return ERROR_SUCCESS;
7261 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7263 MSIPACKAGE *package = param;
7264 LPCWSTR name, value, component;
7265 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
7266 DWORD flags, type, size, len, len_value = 0;
7267 UINT res;
7268 HKEY env = NULL;
7269 MSICOMPONENT *comp;
7270 MSIRECORD *uirow;
7271 int action = 0, found = 0;
7273 component = MSI_RecordGetString(rec, 4);
7274 comp = msi_get_loaded_component(package, component);
7275 if (!comp)
7276 return ERROR_SUCCESS;
7278 comp->Action = msi_get_component_action( package, comp );
7279 if (comp->Action != INSTALLSTATE_LOCAL)
7281 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7282 return ERROR_SUCCESS;
7284 name = MSI_RecordGetString(rec, 2);
7285 value = MSI_RecordGetString(rec, 3);
7287 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7289 res = env_parse_flags(&name, &value, &flags);
7290 if (res != ERROR_SUCCESS || !value)
7291 goto done;
7293 if (value && !deformat_string(package, value, &deformatted))
7295 res = ERROR_OUTOFMEMORY;
7296 goto done;
7299 if ((value = deformatted))
7301 if (flags & ENV_MOD_PREFIX)
7303 p = strrchrW( value, ';' );
7304 len_value = p - value;
7306 else if (flags & ENV_MOD_APPEND)
7308 value = strchrW( value, ';' ) + 1;
7309 len_value = strlenW( value );
7311 else len_value = strlenW( value );
7314 res = open_env_key( flags, &env );
7315 if (res != ERROR_SUCCESS)
7316 goto done;
7318 if (flags & ENV_MOD_MACHINE)
7319 action |= 0x20000000;
7321 size = 0;
7322 type = REG_SZ;
7323 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7324 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7325 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7326 goto done;
7328 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7330 action = 0x2;
7332 /* Nothing to do. */
7333 if (!value)
7335 res = ERROR_SUCCESS;
7336 goto done;
7338 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7339 newval = strdupW(value);
7340 if (!newval)
7342 res = ERROR_OUTOFMEMORY;
7343 goto done;
7346 else
7348 action = 0x1;
7350 /* Contrary to MSDN, +-variable to [~];path works */
7351 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7353 res = ERROR_SUCCESS;
7354 goto done;
7357 if (!(p = q = data = msi_alloc( size )))
7359 msi_free(deformatted);
7360 RegCloseKey(env);
7361 return ERROR_OUTOFMEMORY;
7364 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7365 if (res != ERROR_SUCCESS)
7366 goto done;
7368 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7370 action = 0x4;
7371 res = RegDeleteValueW(env, name);
7372 if (res != ERROR_SUCCESS)
7373 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7374 goto done;
7377 for (;;)
7379 while (*q && *q != ';') q++;
7380 len = q - p;
7381 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7382 (!p[len] || p[len] == ';'))
7384 found = 1;
7385 break;
7387 if (!*q) break;
7388 p = ++q;
7391 if (found)
7393 TRACE("string already set\n");
7394 goto done;
7397 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7398 if (!(p = newval = msi_alloc( size )))
7400 res = ERROR_OUTOFMEMORY;
7401 goto done;
7404 if (flags & ENV_MOD_PREFIX)
7406 memcpy( newval, value, len_value * sizeof(WCHAR) );
7407 newval[len_value] = ';';
7408 p = newval + len_value + 1;
7409 action |= 0x80000000;
7412 strcpyW( p, data );
7414 if (flags & ENV_MOD_APPEND)
7416 p += strlenW( data );
7417 *p++ = ';';
7418 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7419 action |= 0x40000000;
7422 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7423 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7424 if (res)
7426 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7429 done:
7430 uirow = MSI_CreateRecord( 3 );
7431 MSI_RecordSetStringW( uirow, 1, name );
7432 MSI_RecordSetStringW( uirow, 2, newval );
7433 MSI_RecordSetInteger( uirow, 3, action );
7434 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7435 msiobj_release( &uirow->hdr );
7437 if (env) RegCloseKey(env);
7438 msi_free(deformatted);
7439 msi_free(data);
7440 msi_free(newval);
7441 return res;
7444 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7446 static const WCHAR query[] = {
7447 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7448 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7449 MSIQUERY *view;
7450 UINT rc;
7452 if (package->script == SCRIPT_NONE)
7453 return msi_schedule_action(package, SCRIPT_INSTALL, szWriteEnvironmentStrings);
7455 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7456 if (rc != ERROR_SUCCESS)
7457 return ERROR_SUCCESS;
7459 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7460 msiobj_release(&view->hdr);
7461 return rc;
7464 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7466 MSIPACKAGE *package = param;
7467 LPCWSTR name, value, component;
7468 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7469 DWORD flags, type, size, len, len_value = 0, len_new_value;
7470 HKEY env;
7471 MSICOMPONENT *comp;
7472 MSIRECORD *uirow;
7473 int action = 0;
7474 LONG res;
7475 UINT r;
7477 component = MSI_RecordGetString( rec, 4 );
7478 comp = msi_get_loaded_component( package, component );
7479 if (!comp)
7480 return ERROR_SUCCESS;
7482 comp->Action = msi_get_component_action( package, comp );
7483 if (comp->Action != INSTALLSTATE_ABSENT)
7485 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7486 return ERROR_SUCCESS;
7488 name = MSI_RecordGetString( rec, 2 );
7489 value = MSI_RecordGetString( rec, 3 );
7491 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7493 r = env_parse_flags( &name, &value, &flags );
7494 if (r != ERROR_SUCCESS)
7495 return r;
7497 if (!(flags & ENV_ACT_REMOVE))
7499 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7500 return ERROR_SUCCESS;
7503 if (value && !deformat_string( package, value, &deformatted ))
7504 return ERROR_OUTOFMEMORY;
7506 if ((value = deformatted))
7508 if (flags & ENV_MOD_PREFIX)
7510 p = strchrW( value, ';' );
7511 len_value = p - value;
7513 else if (flags & ENV_MOD_APPEND)
7515 value = strchrW( value, ';' ) + 1;
7516 len_value = strlenW( value );
7518 else len_value = strlenW( value );
7521 r = open_env_key( flags, &env );
7522 if (r != ERROR_SUCCESS)
7524 r = ERROR_SUCCESS;
7525 goto done;
7528 if (flags & ENV_MOD_MACHINE)
7529 action |= 0x20000000;
7531 size = 0;
7532 type = REG_SZ;
7533 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7534 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7535 goto done;
7537 if (!(new_value = msi_alloc( size ))) goto done;
7539 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7540 if (res != ERROR_SUCCESS)
7541 goto done;
7543 len_new_value = size / sizeof(WCHAR) - 1;
7544 p = q = new_value;
7545 for (;;)
7547 while (*q && *q != ';') q++;
7548 len = q - p;
7549 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7551 if (*q == ';') q++;
7552 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7553 break;
7555 if (!*q) break;
7556 p = ++q;
7559 if (!new_value[0] || !value)
7561 TRACE("removing %s\n", debugstr_w(name));
7562 res = RegDeleteValueW( env, name );
7563 if (res != ERROR_SUCCESS)
7564 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7566 else
7568 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7569 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7570 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7571 if (res != ERROR_SUCCESS)
7572 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7575 done:
7576 uirow = MSI_CreateRecord( 3 );
7577 MSI_RecordSetStringW( uirow, 1, name );
7578 MSI_RecordSetStringW( uirow, 2, value );
7579 MSI_RecordSetInteger( uirow, 3, action );
7580 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7581 msiobj_release( &uirow->hdr );
7583 if (env) RegCloseKey( env );
7584 msi_free( deformatted );
7585 msi_free( new_value );
7586 return r;
7589 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7591 static const WCHAR query[] = {
7592 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7593 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7594 MSIQUERY *view;
7595 UINT rc;
7597 if (package->script == SCRIPT_NONE)
7598 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveEnvironmentStrings);
7600 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7601 if (rc != ERROR_SUCCESS)
7602 return ERROR_SUCCESS;
7604 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7605 msiobj_release( &view->hdr );
7606 return rc;
7609 UINT msi_validate_product_id( MSIPACKAGE *package )
7611 LPWSTR key, template, id;
7612 UINT r = ERROR_SUCCESS;
7614 id = msi_dup_property( package->db, szProductID );
7615 if (id)
7617 msi_free( id );
7618 return ERROR_SUCCESS;
7620 template = msi_dup_property( package->db, szPIDTemplate );
7621 key = msi_dup_property( package->db, szPIDKEY );
7622 if (key && template)
7624 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7625 r = msi_set_property( package->db, szProductID, key, -1 );
7627 msi_free( template );
7628 msi_free( key );
7629 return r;
7632 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7634 return msi_validate_product_id( package );
7637 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7639 TRACE("\n");
7640 package->need_reboot_at_end = 1;
7641 return ERROR_SUCCESS;
7644 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7646 static const WCHAR szAvailableFreeReg[] =
7647 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7648 MSIRECORD *uirow;
7649 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7651 TRACE("%p %d kilobytes\n", package, space);
7653 uirow = MSI_CreateRecord( 1 );
7654 MSI_RecordSetInteger( uirow, 1, space );
7655 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7656 msiobj_release( &uirow->hdr );
7658 return ERROR_SUCCESS;
7661 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7663 TRACE("%p\n", package);
7665 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7666 return ERROR_SUCCESS;
7669 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7671 FIXME("%p\n", package);
7672 return ERROR_SUCCESS;
7675 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7677 static const WCHAR driver_query[] = {
7678 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7679 'O','D','B','C','D','r','i','v','e','r',0};
7680 static const WCHAR translator_query[] = {
7681 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7682 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7683 MSIQUERY *view;
7684 UINT r, count;
7686 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7687 if (r == ERROR_SUCCESS)
7689 count = 0;
7690 r = MSI_IterateRecords( view, &count, NULL, package );
7691 msiobj_release( &view->hdr );
7692 if (r != ERROR_SUCCESS)
7693 return r;
7694 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7696 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7697 if (r == ERROR_SUCCESS)
7699 count = 0;
7700 r = MSI_IterateRecords( view, &count, NULL, package );
7701 msiobj_release( &view->hdr );
7702 if (r != ERROR_SUCCESS)
7703 return r;
7704 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7706 return ERROR_SUCCESS;
7709 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7711 static const WCHAR fmtW[] =
7712 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7713 'R','E','M','O','V','E','=','%','s',0};
7714 MSIPACKAGE *package = param;
7715 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7716 int attrs = MSI_RecordGetInteger( rec, 5 );
7717 UINT len = ARRAY_SIZE( fmtW );
7718 WCHAR *product, *features, *cmd;
7719 STARTUPINFOW si;
7720 PROCESS_INFORMATION info;
7721 BOOL ret;
7723 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7724 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7726 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7728 len += strlenW( product );
7729 if (features)
7730 len += strlenW( features );
7731 else
7732 len += ARRAY_SIZE( szAll );
7734 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7736 msi_free( product );
7737 msi_free( features );
7738 return ERROR_OUTOFMEMORY;
7740 sprintfW( cmd, fmtW, product, features ? features : szAll );
7741 msi_free( product );
7742 msi_free( features );
7744 memset( &si, 0, sizeof(STARTUPINFOW) );
7745 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7746 msi_free( cmd );
7747 if (!ret) return GetLastError();
7748 CloseHandle( info.hThread );
7750 WaitForSingleObject( info.hProcess, INFINITE );
7751 CloseHandle( info.hProcess );
7752 return ERROR_SUCCESS;
7755 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7757 static const WCHAR query[] = {
7758 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7759 MSIQUERY *view;
7760 UINT r;
7762 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7763 if (r == ERROR_SUCCESS)
7765 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7766 msiobj_release( &view->hdr );
7767 if (r != ERROR_SUCCESS)
7768 return r;
7770 return ERROR_SUCCESS;
7773 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7775 MSIPACKAGE *package = param;
7776 int attributes = MSI_RecordGetInteger( rec, 5 );
7778 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7780 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7781 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7782 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7783 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7784 HKEY hkey;
7785 UINT r;
7787 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7789 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7790 if (r != ERROR_SUCCESS)
7791 return ERROR_SUCCESS;
7793 else
7795 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7796 if (r != ERROR_SUCCESS)
7797 return ERROR_SUCCESS;
7799 RegCloseKey( hkey );
7801 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7802 debugstr_w(upgrade_code), debugstr_w(version_min),
7803 debugstr_w(version_max), debugstr_w(language));
7805 return ERROR_SUCCESS;
7808 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7810 static const WCHAR query[] = {
7811 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7812 'U','p','g','r','a','d','e',0};
7813 MSIQUERY *view;
7814 UINT r;
7816 if (msi_get_property_int( package->db, szInstalled, 0 ))
7818 TRACE("product is installed, skipping action\n");
7819 return ERROR_SUCCESS;
7821 if (msi_get_property_int( package->db, szPreselected, 0 ))
7823 TRACE("Preselected property is set, not migrating feature states\n");
7824 return ERROR_SUCCESS;
7826 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7827 if (r == ERROR_SUCCESS)
7829 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7830 msiobj_release( &view->hdr );
7831 if (r != ERROR_SUCCESS)
7832 return r;
7834 return ERROR_SUCCESS;
7837 static void bind_image( const char *filename, const char *path )
7839 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7841 WARN("failed to bind image %u\n", GetLastError());
7845 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7847 UINT i;
7848 MSIFILE *file;
7849 MSIPACKAGE *package = param;
7850 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7851 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7852 char *filenameA, *pathA;
7853 WCHAR *pathW, **path_list;
7855 if (!(file = msi_get_loaded_file( package, key )))
7857 WARN("file %s not found\n", debugstr_w(key));
7858 return ERROR_SUCCESS;
7860 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7861 path_list = msi_split_string( paths, ';' );
7862 if (!path_list) bind_image( filenameA, NULL );
7863 else
7865 for (i = 0; path_list[i] && path_list[i][0]; i++)
7867 deformat_string( package, path_list[i], &pathW );
7868 if ((pathA = strdupWtoA( pathW )))
7870 bind_image( filenameA, pathA );
7871 msi_free( pathA );
7873 msi_free( pathW );
7876 msi_free( path_list );
7877 msi_free( filenameA );
7878 return ERROR_SUCCESS;
7881 static UINT ACTION_BindImage( MSIPACKAGE *package )
7883 static const WCHAR query[] = {
7884 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7885 'B','i','n','d','I','m','a','g','e',0};
7886 MSIQUERY *view;
7887 UINT r;
7889 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7890 if (r == ERROR_SUCCESS)
7892 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7893 msiobj_release( &view->hdr );
7894 if (r != ERROR_SUCCESS)
7895 return r;
7897 return ERROR_SUCCESS;
7900 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7902 static const WCHAR query[] = {
7903 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7904 MSIQUERY *view;
7905 DWORD count = 0;
7906 UINT r;
7908 r = MSI_OpenQuery( package->db, &view, query, table );
7909 if (r == ERROR_SUCCESS)
7911 r = MSI_IterateRecords(view, &count, NULL, package);
7912 msiobj_release(&view->hdr);
7913 if (r != ERROR_SUCCESS)
7914 return r;
7916 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7917 return ERROR_SUCCESS;
7920 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7922 static const WCHAR table[] = {
7923 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7924 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7927 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7929 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7930 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7933 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7935 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7936 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7939 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7941 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7942 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7945 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7947 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7948 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7951 static const struct
7953 const WCHAR *action;
7954 const UINT description;
7955 const UINT template;
7956 UINT (*handler)(MSIPACKAGE *);
7957 const WCHAR *action_rollback;
7959 StandardActions[] =
7961 { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7962 { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7963 { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7964 { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7965 { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7966 { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7967 { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
7968 { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
7969 { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
7970 { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
7971 { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7972 { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
7973 { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7974 { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7975 { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
7976 { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7977 { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
7978 { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
7979 { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
7980 { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
7981 { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
7982 { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
7983 { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
7984 { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7985 { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7986 { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
7987 { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7988 { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7989 { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7990 { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7991 { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7992 { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7993 { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
7994 { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
7995 { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
7996 { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
7997 { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7998 { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
7999 { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
8000 { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
8001 { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
8002 { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
8003 { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
8004 { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
8005 { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
8006 { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
8007 { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
8008 { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
8009 { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
8010 { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
8011 { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
8012 { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
8013 { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
8014 { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
8015 { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
8016 { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
8017 { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
8018 { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
8019 { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
8020 { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
8021 { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
8022 { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
8023 { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
8024 { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
8025 { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
8026 { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
8027 { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
8028 { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
8029 { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
8030 { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
8031 { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
8032 { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
8033 { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
8034 { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
8035 { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
8036 { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
8037 { szINSTALL, 0, 0, ACTION_INSTALL, NULL },
8038 { 0 }
8041 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
8043 UINT rc = ERROR_FUNCTION_NOT_CALLED;
8044 void *cookie;
8045 UINT i;
8047 if (is_wow64 && package->platform == PLATFORM_X64)
8048 Wow64DisableWow64FsRedirection(&cookie);
8050 i = 0;
8051 while (StandardActions[i].action != NULL)
8053 if (!strcmpW( StandardActions[i].action, action ))
8055 WCHAR description[100] = {0}, template[100] = {0};
8057 if (StandardActions[i].description != 0)
8058 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
8059 if (StandardActions[i].template != 0)
8060 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
8062 ui_actionstart(package, action, description, template);
8063 if (StandardActions[i].handler)
8065 ui_actioninfo( package, action, TRUE, 0 );
8066 rc = StandardActions[i].handler( package );
8067 ui_actioninfo( package, action, FALSE, !rc );
8069 if (StandardActions[i].action_rollback && !package->need_rollback)
8071 TRACE("scheduling rollback action\n");
8072 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
8075 else
8077 FIXME("unhandled standard action %s\n", debugstr_w(action));
8078 rc = ERROR_SUCCESS;
8080 break;
8082 i++;
8085 if (is_wow64 && package->platform == PLATFORM_X64)
8086 Wow64RevertWow64FsRedirection(cookie);
8088 return rc;
8091 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
8093 UINT rc;
8095 TRACE("Performing action (%s)\n", debugstr_w(action));
8097 package->action_progress_increment = 0;
8098 rc = ACTION_HandleStandardAction(package, action);
8100 if (rc == ERROR_FUNCTION_NOT_CALLED)
8101 rc = ACTION_HandleCustomAction(package, action);
8103 if (rc == ERROR_FUNCTION_NOT_CALLED)
8104 WARN("unhandled msi action %s\n", debugstr_w(action));
8106 return rc;
8109 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
8111 UINT rc = ERROR_SUCCESS;
8112 MSIRECORD *row;
8114 static const WCHAR query[] =
8115 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8116 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
8117 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
8118 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
8119 static const WCHAR ui_query[] =
8120 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8121 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
8122 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
8123 ' ', '=',' ','%','i',0};
8125 if (needs_ui_sequence(package))
8126 row = MSI_QueryGetRecord(package->db, ui_query, seq);
8127 else
8128 row = MSI_QueryGetRecord(package->db, query, seq);
8130 if (row)
8132 LPCWSTR action, cond;
8134 TRACE("Running the actions\n");
8136 /* check conditions */
8137 cond = MSI_RecordGetString(row, 2);
8139 /* this is a hack to skip errors in the condition code */
8140 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
8142 msiobj_release(&row->hdr);
8143 return ERROR_SUCCESS;
8146 action = MSI_RecordGetString(row, 1);
8147 if (!action)
8149 ERR("failed to fetch action\n");
8150 msiobj_release(&row->hdr);
8151 return ERROR_FUNCTION_FAILED;
8154 rc = ACTION_PerformAction(package, action);
8156 msiobj_release(&row->hdr);
8159 return rc;
8162 /****************************************************
8163 * TOP level entry points
8164 *****************************************************/
8166 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
8167 LPCWSTR szCommandLine )
8169 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
8170 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
8171 WCHAR *reinstall = NULL, *productcode, *action;
8172 UINT rc;
8173 DWORD len = 0;
8175 if (szPackagePath)
8177 LPWSTR p, dir;
8178 LPCWSTR file;
8180 dir = strdupW(szPackagePath);
8181 p = strrchrW(dir, '\\');
8182 if (p)
8184 *(++p) = 0;
8185 file = szPackagePath + (p - dir);
8187 else
8189 msi_free(dir);
8190 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
8191 GetCurrentDirectoryW(MAX_PATH, dir);
8192 lstrcatW(dir, szBackSlash);
8193 file = szPackagePath;
8196 msi_free( package->PackagePath );
8197 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8198 if (!package->PackagePath)
8200 msi_free(dir);
8201 return ERROR_OUTOFMEMORY;
8204 lstrcpyW(package->PackagePath, dir);
8205 lstrcatW(package->PackagePath, file);
8206 msi_free(dir);
8208 msi_set_sourcedir_props(package, FALSE);
8211 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8212 if (rc != ERROR_SUCCESS)
8213 return rc;
8215 msi_apply_transforms( package );
8216 msi_apply_patches( package );
8218 if (msi_get_property( package->db, szAction, NULL, &len ))
8219 msi_set_property( package->db, szAction, szINSTALL, -1 );
8220 action = msi_dup_property( package->db, szAction );
8221 CharUpperW(action);
8223 msi_set_original_database_property( package->db, szPackagePath );
8224 msi_parse_command_line( package, szCommandLine, FALSE );
8225 msi_adjust_privilege_properties( package );
8226 msi_set_context( package );
8228 productcode = msi_dup_property( package->db, szProductCode );
8229 if (strcmpiW( productcode, package->ProductCode ))
8231 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
8232 msi_free( package->ProductCode );
8233 package->ProductCode = productcode;
8235 else msi_free( productcode );
8237 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
8239 TRACE("disabling rollback\n");
8240 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8243 rc = ACTION_PerformAction(package, action);
8245 /* process the ending type action */
8246 if (rc == ERROR_SUCCESS)
8247 ACTION_PerformActionSequence(package, -1);
8248 else if (rc == ERROR_INSTALL_USEREXIT)
8249 ACTION_PerformActionSequence(package, -2);
8250 else if (rc == ERROR_INSTALL_SUSPEND)
8251 ACTION_PerformActionSequence(package, -4);
8252 else /* failed */
8254 ACTION_PerformActionSequence(package, -3);
8255 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8257 package->need_rollback = TRUE;
8261 /* finish up running custom actions */
8262 ACTION_FinishCustomActions(package);
8264 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
8266 WARN("installation failed, running rollback script\n");
8267 execute_script( package, SCRIPT_ROLLBACK );
8269 msi_free( reinstall );
8270 msi_free( action );
8272 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8273 return ERROR_SUCCESS_REBOOT_REQUIRED;
8275 return rc;