msi: Allow recursive changes to the target folder path.
[wine.git] / dlls / msi / action.c
blob0d6da3e2cc1072a009742b933aa3aa452ffdf7b9
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 SYSTEMTIME systime;
5181 DWORD size, langid;
5182 WCHAR date[9], *val, *buffer;
5183 const WCHAR *prop, *key;
5185 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5186 static const WCHAR modpath_fmt[] =
5187 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5188 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5189 static const WCHAR szModifyPath[] =
5190 {'M','o','d','i','f','y','P','a','t','h',0};
5191 static const WCHAR szUninstallString[] =
5192 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5193 static const WCHAR szEstimatedSize[] =
5194 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5195 static const WCHAR szDisplayVersion[] =
5196 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5197 static const WCHAR szInstallSource[] =
5198 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5199 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5200 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5201 static const WCHAR szAuthorizedCDFPrefix[] =
5202 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5203 static const WCHAR szARPCONTACT[] =
5204 {'A','R','P','C','O','N','T','A','C','T',0};
5205 static const WCHAR szContact[] =
5206 {'C','o','n','t','a','c','t',0};
5207 static const WCHAR szARPCOMMENTS[] =
5208 {'A','R','P','C','O','M','M','E','N','T','S',0};
5209 static const WCHAR szComments[] =
5210 {'C','o','m','m','e','n','t','s',0};
5211 static const WCHAR szProductName[] =
5212 {'P','r','o','d','u','c','t','N','a','m','e',0};
5213 static const WCHAR szDisplayName[] =
5214 {'D','i','s','p','l','a','y','N','a','m','e',0};
5215 static const WCHAR szARPHELPLINK[] =
5216 {'A','R','P','H','E','L','P','L','I','N','K',0};
5217 static const WCHAR szHelpLink[] =
5218 {'H','e','l','p','L','i','n','k',0};
5219 static const WCHAR szARPHELPTELEPHONE[] =
5220 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5221 static const WCHAR szHelpTelephone[] =
5222 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5223 static const WCHAR szARPINSTALLLOCATION[] =
5224 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5225 static const WCHAR szManufacturer[] =
5226 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5227 static const WCHAR szPublisher[] =
5228 {'P','u','b','l','i','s','h','e','r',0};
5229 static const WCHAR szARPREADME[] =
5230 {'A','R','P','R','E','A','D','M','E',0};
5231 static const WCHAR szReadme[] =
5232 {'R','e','a','d','M','e',0};
5233 static const WCHAR szARPSIZE[] =
5234 {'A','R','P','S','I','Z','E',0};
5235 static const WCHAR szSize[] =
5236 {'S','i','z','e',0};
5237 static const WCHAR szARPURLINFOABOUT[] =
5238 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5239 static const WCHAR szURLInfoAbout[] =
5240 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5241 static const WCHAR szARPURLUPDATEINFO[] =
5242 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5243 static const WCHAR szURLUpdateInfo[] =
5244 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5245 static const WCHAR szARPSYSTEMCOMPONENT[] =
5246 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5247 static const WCHAR szSystemComponent[] =
5248 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5250 static const WCHAR *propval[] = {
5251 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5252 szARPCONTACT, szContact,
5253 szARPCOMMENTS, szComments,
5254 szProductName, szDisplayName,
5255 szARPHELPLINK, szHelpLink,
5256 szARPHELPTELEPHONE, szHelpTelephone,
5257 szARPINSTALLLOCATION, szInstallLocation,
5258 szSourceDir, szInstallSource,
5259 szManufacturer, szPublisher,
5260 szARPREADME, szReadme,
5261 szARPSIZE, szSize,
5262 szARPURLINFOABOUT, szURLInfoAbout,
5263 szARPURLUPDATEINFO, szURLUpdateInfo,
5264 NULL
5266 const WCHAR **p = propval;
5268 while (*p)
5270 prop = *p++;
5271 key = *p++;
5272 val = msi_dup_property(package->db, prop);
5273 msi_reg_set_val_str(hkey, key, val);
5274 msi_free(val);
5277 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5278 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5280 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5282 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5283 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5284 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5285 msi_free(buffer);
5287 /* FIXME: Write real Estimated Size when we have it */
5288 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5290 GetLocalTime(&systime);
5291 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5292 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5294 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5295 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5297 buffer = msi_dup_property(package->db, szProductVersion);
5298 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5299 if (buffer)
5301 DWORD verdword = msi_version_str_to_dword(buffer);
5303 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5304 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5305 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5306 msi_free(buffer);
5309 return ERROR_SUCCESS;
5312 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5314 WCHAR *upgrade_code, squashed_pc[SQUASHED_GUID_SIZE];
5315 MSIRECORD *uirow;
5316 HKEY hkey, props, upgrade_key;
5317 UINT rc;
5319 if (package->script == SCRIPT_NONE)
5320 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterProduct);
5322 /* FIXME: also need to publish if the product is in advertise mode */
5323 if (!msi_get_property_int( package->db, szProductToBeRegistered, 0 )
5324 && !msi_check_publish(package))
5325 return ERROR_SUCCESS;
5327 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5328 if (rc != ERROR_SUCCESS)
5329 return rc;
5331 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5332 if (rc != ERROR_SUCCESS)
5333 goto done;
5335 rc = msi_publish_install_properties(package, hkey);
5336 if (rc != ERROR_SUCCESS)
5337 goto done;
5339 rc = msi_publish_install_properties(package, props);
5340 if (rc != ERROR_SUCCESS)
5341 goto done;
5343 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5344 if (upgrade_code)
5346 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5347 if (rc == ERROR_SUCCESS)
5349 squash_guid( package->ProductCode, squashed_pc );
5350 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5351 RegCloseKey( upgrade_key );
5353 msi_free( upgrade_code );
5355 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5356 package->delete_on_close = FALSE;
5358 done:
5359 uirow = MSI_CreateRecord( 1 );
5360 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5361 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5362 msiobj_release( &uirow->hdr );
5364 RegCloseKey(hkey);
5365 return ERROR_SUCCESS;
5368 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5370 return execute_script(package, SCRIPT_INSTALL);
5373 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5375 MSIPACKAGE *package = param;
5376 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5377 WCHAR *p, *icon_path;
5379 if (!icon) return ERROR_SUCCESS;
5380 if ((icon_path = msi_build_icon_path( package, icon )))
5382 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5383 DeleteFileW( icon_path );
5384 if ((p = strrchrW( icon_path, '\\' )))
5386 *p = 0;
5387 RemoveDirectoryW( icon_path );
5389 msi_free( icon_path );
5391 return ERROR_SUCCESS;
5394 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5396 static const WCHAR query[]= {
5397 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5398 MSIQUERY *view;
5399 UINT r;
5401 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5402 if (r == ERROR_SUCCESS)
5404 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5405 msiobj_release( &view->hdr );
5406 if (r != ERROR_SUCCESS)
5407 return r;
5409 return ERROR_SUCCESS;
5412 static void remove_product_upgrade_code( MSIPACKAGE *package )
5414 WCHAR *code, product[SQUASHED_GUID_SIZE];
5415 HKEY hkey;
5416 LONG res;
5417 DWORD count;
5419 squash_guid( package->ProductCode, product );
5420 if (!(code = msi_dup_property( package->db, szUpgradeCode )))
5422 WARN( "upgrade code not found\n" );
5423 return;
5425 if (!MSIREG_OpenUpgradeCodesKey( code, &hkey, FALSE ))
5427 RegDeleteValueW( hkey, product );
5428 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5429 RegCloseKey( hkey );
5430 if (!res && !count) MSIREG_DeleteUpgradeCodesKey( code );
5432 if (!MSIREG_OpenUserUpgradeCodesKey( code, &hkey, FALSE ))
5434 RegDeleteValueW( hkey, product );
5435 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5436 RegCloseKey( hkey );
5437 if (!res && !count) MSIREG_DeleteUserUpgradeCodesKey( code );
5439 if (!MSIREG_OpenClassesUpgradeCodesKey( code, &hkey, FALSE ))
5441 RegDeleteValueW( hkey, product );
5442 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL );
5443 RegCloseKey( hkey );
5444 if (!res && !count) MSIREG_DeleteClassesUpgradeCodesKey( code );
5447 msi_free( code );
5450 static UINT ACTION_UnpublishProduct(MSIPACKAGE *package)
5452 MSIPATCHINFO *patch;
5454 MSIREG_DeleteProductKey(package->ProductCode);
5455 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5456 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5458 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5459 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5460 MSIREG_DeleteUserProductKey(package->ProductCode);
5461 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5463 remove_product_upgrade_code( package );
5465 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5467 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5468 if (!strcmpW( package->ProductCode, patch->products ))
5470 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5471 patch->delete_on_close = TRUE;
5473 /* FIXME: remove local patch package if this is the last product */
5475 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5476 package->delete_on_close = TRUE;
5478 msi_unpublish_icons( package );
5479 return ERROR_SUCCESS;
5482 static BOOL is_full_uninstall( MSIPACKAGE *package )
5484 MSIFEATURE *feature;
5486 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5488 if (feature->Action != INSTALLSTATE_ABSENT &&
5489 (feature->Installed != INSTALLSTATE_ABSENT || feature->Action != INSTALLSTATE_UNKNOWN))
5490 return FALSE;
5493 return TRUE;
5496 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5498 UINT rc;
5500 /* first do the same as an InstallExecute */
5501 rc = execute_script(package, SCRIPT_INSTALL);
5502 if (rc != ERROR_SUCCESS)
5503 return rc;
5505 /* then handle commit actions */
5506 rc = execute_script(package, SCRIPT_COMMIT);
5507 if (rc != ERROR_SUCCESS)
5508 return rc;
5510 if (is_full_uninstall(package))
5511 rc = ACTION_UnpublishProduct(package);
5513 return rc;
5516 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5518 static const WCHAR RunOnce[] = {
5519 'S','o','f','t','w','a','r','e','\\',
5520 'M','i','c','r','o','s','o','f','t','\\',
5521 'W','i','n','d','o','w','s','\\',
5522 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5523 'R','u','n','O','n','c','e',0};
5524 static const WCHAR InstallRunOnce[] = {
5525 'S','o','f','t','w','a','r','e','\\',
5526 'M','i','c','r','o','s','o','f','t','\\',
5527 'W','i','n','d','o','w','s','\\',
5528 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5529 'I','n','s','t','a','l','l','e','r','\\',
5530 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5532 static const WCHAR msiexec_fmt[] = {
5533 '%','s',
5534 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5535 '\"','%','s','\"',0};
5536 static const WCHAR install_fmt[] = {
5537 '/','I',' ','\"','%','s','\"',' ',
5538 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5539 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5540 WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE];
5541 HKEY hkey;
5543 squash_guid( package->ProductCode, squashed_pc );
5545 GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir));
5546 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5547 snprintfW(buffer, ARRAY_SIZE(buffer), msiexec_fmt, sysdir, squashed_pc);
5549 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5550 RegCloseKey(hkey);
5552 TRACE("Reboot command %s\n",debugstr_w(buffer));
5554 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5555 sprintfW( buffer, install_fmt, package->ProductCode, squashed_pc );
5557 msi_reg_set_val_str( hkey, squashed_pc, buffer );
5558 RegCloseKey(hkey);
5560 return ERROR_INSTALL_SUSPEND;
5563 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5565 DWORD attrib;
5566 UINT rc;
5569 * We are currently doing what should be done here in the top level Install
5570 * however for Administrative and uninstalls this step will be needed
5572 if (!package->PackagePath)
5573 return ERROR_SUCCESS;
5575 msi_set_sourcedir_props(package, TRUE);
5577 attrib = GetFileAttributesW(package->db->path);
5578 if (attrib == INVALID_FILE_ATTRIBUTES)
5580 MSIRECORD *record;
5581 LPWSTR prompt;
5582 DWORD size = 0;
5584 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5585 package->Context, MSICODE_PRODUCT,
5586 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5587 if (rc == ERROR_MORE_DATA)
5589 prompt = msi_alloc(size * sizeof(WCHAR));
5590 MsiSourceListGetInfoW(package->ProductCode, NULL,
5591 package->Context, MSICODE_PRODUCT,
5592 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5594 else
5595 prompt = strdupW(package->db->path);
5597 record = MSI_CreateRecord(2);
5598 MSI_RecordSetInteger(record, 1, MSIERR_INSERTDISK);
5599 MSI_RecordSetStringW(record, 2, prompt);
5600 msi_free(prompt);
5601 while(attrib == INVALID_FILE_ATTRIBUTES)
5603 MSI_RecordSetStringW(record, 0, NULL);
5604 rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ERROR, record);
5605 if (rc == IDCANCEL)
5606 return ERROR_INSTALL_USEREXIT;
5607 attrib = GetFileAttributesW(package->db->path);
5609 rc = ERROR_SUCCESS;
5611 else
5612 return ERROR_SUCCESS;
5614 return rc;
5617 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5619 HKEY hkey = 0;
5620 LPWSTR buffer, productid = NULL;
5621 UINT i, rc = ERROR_SUCCESS;
5622 MSIRECORD *uirow;
5624 static const WCHAR szPropKeys[][80] =
5626 {'P','r','o','d','u','c','t','I','D',0},
5627 {'U','S','E','R','N','A','M','E',0},
5628 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5629 {0},
5632 static const WCHAR szRegKeys[][80] =
5634 {'P','r','o','d','u','c','t','I','D',0},
5635 {'R','e','g','O','w','n','e','r',0},
5636 {'R','e','g','C','o','m','p','a','n','y',0},
5637 {0},
5640 if (package->script == SCRIPT_NONE)
5641 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterUser);
5643 if (msi_check_unpublish(package))
5645 MSIREG_DeleteUserDataProductKey(package->ProductCode, package->Context);
5646 goto end;
5649 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5650 if (!productid)
5651 goto end;
5653 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5654 NULL, &hkey, TRUE);
5655 if (rc != ERROR_SUCCESS)
5656 goto end;
5658 for( i = 0; szPropKeys[i][0]; i++ )
5660 buffer = msi_dup_property( package->db, szPropKeys[i] );
5661 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5662 msi_free( buffer );
5665 end:
5666 uirow = MSI_CreateRecord( 1 );
5667 MSI_RecordSetStringW( uirow, 1, productid );
5668 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5669 msiobj_release( &uirow->hdr );
5671 msi_free(productid);
5672 RegCloseKey(hkey);
5673 return rc;
5676 static UINT iterate_properties(MSIRECORD *record, void *param)
5678 static const WCHAR prop_template[] =
5679 {'P','r','o','p','e','r','t','y','(','S',')',':',' ','[','1',']',' ','=',' ','[','2',']',0};
5680 MSIRECORD *uirow;
5682 uirow = MSI_CloneRecord(record);
5683 if (!uirow) return ERROR_OUTOFMEMORY;
5684 MSI_RecordSetStringW(uirow, 0, prop_template);
5685 MSI_ProcessMessage(param, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow);
5686 msiobj_release(&uirow->hdr);
5688 return ERROR_SUCCESS;
5692 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5694 static const WCHAR prop_query[] =
5695 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','_','P','r','o','p','e','r','t','y','`',0};
5696 WCHAR *productname;
5697 WCHAR *action;
5698 WCHAR *info_template;
5699 MSIQUERY *view;
5700 MSIRECORD *uirow, *uirow_info;
5701 UINT rc;
5703 /* Send COMMONDATA and INFO messages. */
5704 /* FIXME: when should these messages be sent? [see also MsiOpenPackage()] */
5705 uirow = MSI_CreateRecord(3);
5706 if (!uirow) return ERROR_OUTOFMEMORY;
5707 MSI_RecordSetStringW(uirow, 0, NULL);
5708 MSI_RecordSetInteger(uirow, 1, 0);
5709 MSI_RecordSetInteger(uirow, 2, package->num_langids ? package->langids[0] : 0);
5710 MSI_RecordSetInteger(uirow, 3, msi_get_string_table_codepage(package->db->strings));
5711 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5712 /* FIXME: send INSTALLMESSAGE_PROGRESS */
5713 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5715 if (!(needs_ui_sequence(package) && ui_sequence_exists(package)))
5717 uirow_info = MSI_CreateRecord(0);
5718 if (!uirow_info)
5720 msiobj_release(&uirow->hdr);
5721 return ERROR_OUTOFMEMORY;
5723 info_template = msi_get_error_message(package->db, MSIERR_INFO_LOGGINGSTART);
5724 MSI_RecordSetStringW(uirow_info, 0, info_template);
5725 msi_free(info_template);
5726 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO|MB_ICONHAND, uirow_info);
5727 msiobj_release(&uirow_info->hdr);
5730 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5732 productname = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
5733 MSI_RecordSetInteger(uirow, 1, 1);
5734 MSI_RecordSetStringW(uirow, 2, productname);
5735 MSI_RecordSetStringW(uirow, 3, NULL);
5736 MSI_ProcessMessage(package, INSTALLMESSAGE_COMMONDATA, uirow);
5737 msiobj_release(&uirow->hdr);
5739 package->LastActionResult = MSI_NULL_INTEGER;
5741 action = msi_dup_property(package->db, szEXECUTEACTION);
5742 if (!action) action = msi_strdupW(szINSTALL, strlenW(szINSTALL));
5744 /* Perform the action. Top-level actions trigger a sequence. */
5745 if (!strcmpW(action, szINSTALL))
5747 /* Send ACTIONSTART/INFO and INSTALLSTART. */
5748 ui_actionstart(package, szINSTALL, NULL, NULL);
5749 ui_actioninfo(package, szINSTALL, TRUE, 0);
5750 uirow = MSI_CreateRecord(2);
5751 if (!uirow)
5753 rc = ERROR_OUTOFMEMORY;
5754 goto end;
5756 MSI_RecordSetStringW(uirow, 0, NULL);
5757 MSI_RecordSetStringW(uirow, 1, productname);
5758 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5759 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLSTART, uirow);
5760 msiobj_release(&uirow->hdr);
5762 /* Perform the installation. Always use the ExecuteSequence. */
5763 package->InWhatSequence |= SEQUENCE_EXEC;
5764 rc = ACTION_ProcessExecSequence(package);
5766 /* Send return value and INSTALLEND. */
5767 ui_actioninfo(package, szINSTALL, FALSE, !rc);
5768 uirow = MSI_CreateRecord(3);
5769 if (!uirow)
5771 rc = ERROR_OUTOFMEMORY;
5772 goto end;
5774 MSI_RecordSetStringW(uirow, 0, NULL);
5775 MSI_RecordSetStringW(uirow, 1, productname);
5776 MSI_RecordSetStringW(uirow, 2, package->ProductCode);
5777 MSI_RecordSetInteger(uirow, 3, !rc);
5778 MSI_ProcessMessage(package, INSTALLMESSAGE_INSTALLEND, uirow);
5779 msiobj_release(&uirow->hdr);
5781 else
5782 rc = ACTION_PerformAction(package, action);
5784 /* Send all set properties. */
5785 if (!MSI_OpenQuery(package->db, &view, prop_query))
5787 MSI_IterateRecords(view, NULL, iterate_properties, package);
5788 msiobj_release(&view->hdr);
5791 /* And finally, toggle the cancel off and on. */
5792 uirow = MSI_CreateRecord(2);
5793 if (!uirow)
5795 rc = ERROR_OUTOFMEMORY;
5796 goto end;
5798 MSI_RecordSetStringW(uirow, 0, NULL);
5799 MSI_RecordSetInteger(uirow, 1, 2);
5800 MSI_RecordSetInteger(uirow, 2, 0);
5801 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5802 MSI_RecordSetInteger(uirow, 2, 1);
5803 MSI_ProcessMessageVerbatim(package, INSTALLMESSAGE_COMMONDATA, uirow);
5804 msiobj_release(&uirow->hdr);
5806 end:
5807 msi_free(productname);
5808 msi_free(action);
5809 return rc;
5812 static UINT ACTION_INSTALL(MSIPACKAGE *package)
5814 msi_set_property(package->db, szEXECUTEACTION, szINSTALL, -1);
5815 if (needs_ui_sequence(package) && ui_sequence_exists(package))
5817 package->InWhatSequence |= SEQUENCE_UI;
5818 return ACTION_ProcessUISequence(package);
5820 else
5821 return ACTION_ExecuteAction(package);
5824 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5826 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5827 WCHAR productid_85[21], component_85[21], *ret;
5828 GUID clsid;
5829 DWORD sz;
5831 /* > is used if there is a component GUID and < if not. */
5833 productid_85[0] = 0;
5834 component_85[0] = 0;
5835 CLSIDFromString( package->ProductCode, &clsid );
5837 encode_base85_guid( &clsid, productid_85 );
5838 if (component)
5840 CLSIDFromString( component->ComponentId, &clsid );
5841 encode_base85_guid( &clsid, component_85 );
5844 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5845 debugstr_w(component_85));
5847 sz = 20 + strlenW( feature ) + 20 + 3;
5848 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5849 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5850 return ret;
5853 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5855 MSIPACKAGE *package = param;
5856 LPCWSTR compgroupid, component, feature, qualifier, text;
5857 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5858 HKEY hkey = NULL;
5859 UINT rc;
5860 MSICOMPONENT *comp;
5861 MSIFEATURE *feat;
5862 DWORD sz;
5863 MSIRECORD *uirow;
5864 int len;
5866 feature = MSI_RecordGetString(rec, 5);
5867 feat = msi_get_loaded_feature(package, feature);
5868 if (!feat)
5869 return ERROR_SUCCESS;
5871 feat->Action = msi_get_feature_action( package, feat );
5872 if (feat->Action != INSTALLSTATE_LOCAL &&
5873 feat->Action != INSTALLSTATE_SOURCE &&
5874 feat->Action != INSTALLSTATE_ADVERTISED)
5876 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5877 return ERROR_SUCCESS;
5880 component = MSI_RecordGetString(rec, 3);
5881 comp = msi_get_loaded_component(package, component);
5882 if (!comp)
5883 return ERROR_SUCCESS;
5885 compgroupid = MSI_RecordGetString(rec,1);
5886 qualifier = MSI_RecordGetString(rec,2);
5888 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5889 if (rc != ERROR_SUCCESS)
5890 goto end;
5892 advertise = msi_create_component_advertise_string( package, comp, feature );
5893 text = MSI_RecordGetString( rec, 4 );
5894 if (text)
5896 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5897 strcpyW( p, advertise );
5898 strcatW( p, text );
5899 msi_free( advertise );
5900 advertise = p;
5902 existing = msi_reg_get_val_str( hkey, qualifier );
5904 sz = strlenW( advertise ) + 1;
5905 if (existing)
5907 for (p = existing; *p; p += len)
5909 len = strlenW( p ) + 1;
5910 if (strcmpW( advertise, p )) sz += len;
5913 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5915 rc = ERROR_OUTOFMEMORY;
5916 goto end;
5918 q = output;
5919 if (existing)
5921 for (p = existing; *p; p += len)
5923 len = strlenW( p ) + 1;
5924 if (strcmpW( advertise, p ))
5926 memcpy( q, p, len * sizeof(WCHAR) );
5927 q += len;
5931 strcpyW( q, advertise );
5932 q[strlenW( q ) + 1] = 0;
5934 msi_reg_set_val_multi_str( hkey, qualifier, output );
5936 end:
5937 RegCloseKey(hkey);
5938 msi_free( output );
5939 msi_free( advertise );
5940 msi_free( existing );
5942 /* the UI chunk */
5943 uirow = MSI_CreateRecord( 2 );
5944 MSI_RecordSetStringW( uirow, 1, compgroupid );
5945 MSI_RecordSetStringW( uirow, 2, qualifier);
5946 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
5947 msiobj_release( &uirow->hdr );
5948 /* FIXME: call ui_progress? */
5950 return rc;
5954 * At present I am ignorning the advertised components part of this and only
5955 * focusing on the qualified component sets
5957 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5959 static const WCHAR query[] = {
5960 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5961 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5962 MSIQUERY *view;
5963 UINT rc;
5965 if (package->script == SCRIPT_NONE)
5966 return msi_schedule_action(package, SCRIPT_INSTALL, szPublishComponents);
5968 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5969 if (rc != ERROR_SUCCESS)
5970 return ERROR_SUCCESS;
5972 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5973 msiobj_release(&view->hdr);
5974 return rc;
5977 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5979 static const WCHAR szInstallerComponents[] = {
5980 'S','o','f','t','w','a','r','e','\\',
5981 'M','i','c','r','o','s','o','f','t','\\',
5982 'I','n','s','t','a','l','l','e','r','\\',
5983 'C','o','m','p','o','n','e','n','t','s','\\',0};
5985 MSIPACKAGE *package = param;
5986 LPCWSTR compgroupid, component, feature, qualifier;
5987 MSICOMPONENT *comp;
5988 MSIFEATURE *feat;
5989 MSIRECORD *uirow;
5990 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5991 LONG res;
5993 feature = MSI_RecordGetString( rec, 5 );
5994 feat = msi_get_loaded_feature( package, feature );
5995 if (!feat)
5996 return ERROR_SUCCESS;
5998 feat->Action = msi_get_feature_action( package, feat );
5999 if (feat->Action != INSTALLSTATE_ABSENT)
6001 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
6002 return ERROR_SUCCESS;
6005 component = MSI_RecordGetString( rec, 3 );
6006 comp = msi_get_loaded_component( package, component );
6007 if (!comp)
6008 return ERROR_SUCCESS;
6010 compgroupid = MSI_RecordGetString( rec, 1 );
6011 qualifier = MSI_RecordGetString( rec, 2 );
6013 squash_guid( compgroupid, squashed );
6014 strcpyW( keypath, szInstallerComponents );
6015 strcatW( keypath, squashed );
6017 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
6018 if (res != ERROR_SUCCESS)
6020 WARN("Unable to delete component key %d\n", res);
6023 uirow = MSI_CreateRecord( 2 );
6024 MSI_RecordSetStringW( uirow, 1, compgroupid );
6025 MSI_RecordSetStringW( uirow, 2, qualifier );
6026 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6027 msiobj_release( &uirow->hdr );
6029 return ERROR_SUCCESS;
6032 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6034 static const WCHAR query[] = {
6035 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6036 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
6037 MSIQUERY *view;
6038 UINT rc;
6040 if (package->script == SCRIPT_NONE)
6041 return msi_schedule_action(package, SCRIPT_INSTALL, szUnpublishComponents);
6043 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6044 if (rc != ERROR_SUCCESS)
6045 return ERROR_SUCCESS;
6047 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
6048 msiobj_release( &view->hdr );
6049 return rc;
6052 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
6054 static const WCHAR query[] =
6055 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6056 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
6057 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
6058 MSIPACKAGE *package = param;
6059 MSICOMPONENT *component;
6060 MSIRECORD *row;
6061 MSIFILE *file;
6062 SC_HANDLE hscm = NULL, service = NULL;
6063 LPCWSTR comp, key;
6064 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
6065 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
6066 DWORD serv_type, start_type, err_control;
6067 BOOL is_vital;
6068 SERVICE_DESCRIPTIONW sd = {NULL};
6069 UINT ret = ERROR_SUCCESS;
6071 comp = MSI_RecordGetString( rec, 12 );
6072 component = msi_get_loaded_component( package, comp );
6073 if (!component)
6075 WARN("service component not found\n");
6076 goto done;
6078 component->Action = msi_get_component_action( package, component );
6079 if (component->Action != INSTALLSTATE_LOCAL)
6081 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
6082 goto done;
6084 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
6085 if (!hscm)
6087 ERR("Failed to open the SC Manager!\n");
6088 goto done;
6091 start_type = MSI_RecordGetInteger(rec, 5);
6092 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
6093 goto done;
6095 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
6096 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
6097 serv_type = MSI_RecordGetInteger(rec, 4);
6098 err_control = MSI_RecordGetInteger(rec, 6);
6099 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
6100 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
6101 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
6102 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
6103 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
6104 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
6106 /* Should the complete install fail if CreateService fails? */
6107 is_vital = (err_control & msidbServiceInstallErrorControlVital);
6109 /* Remove the msidbServiceInstallErrorControlVital-flag from err_control.
6110 CreateService (under Windows) would fail if not. */
6111 err_control &= ~msidbServiceInstallErrorControlVital;
6113 /* fetch the service path */
6114 row = MSI_QueryGetRecord(package->db, query, comp);
6115 if (!row)
6117 ERR("Query failed\n");
6118 goto done;
6120 if (!(key = MSI_RecordGetString(row, 6)))
6122 msiobj_release(&row->hdr);
6123 goto done;
6125 file = msi_get_loaded_file(package, key);
6126 msiobj_release(&row->hdr);
6127 if (!file)
6129 ERR("Failed to load the service file\n");
6130 goto done;
6133 if (!args || !args[0]) image_path = file->TargetPath;
6134 else
6136 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
6137 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
6139 ret = ERROR_OUTOFMEMORY;
6140 goto done;
6143 strcpyW(image_path, file->TargetPath);
6144 strcatW(image_path, szSpace);
6145 strcatW(image_path, args);
6147 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
6148 start_type, err_control, image_path, load_order,
6149 NULL, depends, serv_name, pass);
6151 if (!service)
6153 if (GetLastError() != ERROR_SERVICE_EXISTS)
6155 WARN("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
6156 if (is_vital)
6157 ret = ERROR_INSTALL_FAILURE;
6161 else if (sd.lpDescription)
6163 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
6164 WARN("failed to set service description %u\n", GetLastError());
6167 if (image_path != file->TargetPath) msi_free(image_path);
6168 done:
6169 if (service) CloseServiceHandle(service);
6170 if (hscm) CloseServiceHandle(hscm);
6171 msi_free(name);
6172 msi_free(disp);
6173 msi_free(sd.lpDescription);
6174 msi_free(load_order);
6175 msi_free(serv_name);
6176 msi_free(pass);
6177 msi_free(depends);
6178 msi_free(args);
6180 return ret;
6183 static UINT ACTION_InstallServices( MSIPACKAGE *package )
6185 static const WCHAR query[] = {
6186 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6187 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
6188 MSIQUERY *view;
6189 UINT rc;
6191 if (package->script == SCRIPT_NONE)
6192 return msi_schedule_action(package, SCRIPT_INSTALL, szInstallServices);
6194 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6195 if (rc != ERROR_SUCCESS)
6196 return ERROR_SUCCESS;
6198 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
6199 msiobj_release(&view->hdr);
6200 return rc;
6203 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
6204 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
6206 LPCWSTR *vector, *temp_vector;
6207 LPWSTR p, q;
6208 DWORD sep_len;
6210 static const WCHAR separator[] = {'[','~',']',0};
6212 *numargs = 0;
6213 sep_len = ARRAY_SIZE(separator) - 1;
6215 if (!args)
6216 return NULL;
6218 vector = msi_alloc(sizeof(LPWSTR));
6219 if (!vector)
6220 return NULL;
6222 p = args;
6225 (*numargs)++;
6226 vector[*numargs - 1] = p;
6228 if ((q = strstrW(p, separator)))
6230 *q = '\0';
6232 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
6233 if (!temp_vector)
6235 msi_free(vector);
6236 return NULL;
6238 vector = temp_vector;
6240 p = q + sep_len;
6242 } while (q);
6244 return vector;
6247 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
6249 MSIPACKAGE *package = param;
6250 MSICOMPONENT *comp;
6251 MSIRECORD *uirow;
6252 SC_HANDLE scm = NULL, service = NULL;
6253 LPCWSTR component, *vector = NULL;
6254 LPWSTR name, args, display_name = NULL;
6255 DWORD event, numargs, len, wait, dummy;
6256 UINT r = ERROR_FUNCTION_FAILED;
6257 SERVICE_STATUS_PROCESS status;
6258 ULONGLONG start_time;
6260 component = MSI_RecordGetString(rec, 6);
6261 comp = msi_get_loaded_component(package, component);
6262 if (!comp)
6263 return ERROR_SUCCESS;
6265 event = MSI_RecordGetInteger( rec, 3 );
6266 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6268 comp->Action = msi_get_component_action( package, comp );
6269 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStart)) &&
6270 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStart)))
6272 TRACE("not starting %s\n", debugstr_w(name));
6273 msi_free( name );
6274 return ERROR_SUCCESS;
6277 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
6278 wait = MSI_RecordGetInteger(rec, 5);
6280 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
6281 if (!scm)
6283 ERR("Failed to open the service control manager\n");
6284 goto done;
6287 len = 0;
6288 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6289 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6291 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6292 GetServiceDisplayNameW( scm, name, display_name, &len );
6295 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
6296 if (!service)
6298 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
6299 goto done;
6302 vector = msi_service_args_to_vector(args, &numargs);
6304 if (!StartServiceW(service, numargs, vector) &&
6305 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6307 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6308 goto done;
6311 r = ERROR_SUCCESS;
6312 if (wait)
6314 /* wait for at most 30 seconds for the service to be up and running */
6315 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6316 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6318 TRACE("failed to query service status (%u)\n", GetLastError());
6319 goto done;
6321 start_time = GetTickCount64();
6322 while (status.dwCurrentState == SERVICE_START_PENDING)
6324 if (GetTickCount64() - start_time > 30000) break;
6325 Sleep(1000);
6326 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6327 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6329 TRACE("failed to query service status (%u)\n", GetLastError());
6330 goto done;
6333 if (status.dwCurrentState != SERVICE_RUNNING)
6335 WARN("service failed to start %u\n", status.dwCurrentState);
6336 r = ERROR_FUNCTION_FAILED;
6340 done:
6341 uirow = MSI_CreateRecord( 2 );
6342 MSI_RecordSetStringW( uirow, 1, display_name );
6343 MSI_RecordSetStringW( uirow, 2, name );
6344 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6345 msiobj_release( &uirow->hdr );
6347 if (service) CloseServiceHandle(service);
6348 if (scm) CloseServiceHandle(scm);
6350 msi_free(name);
6351 msi_free(args);
6352 msi_free(vector);
6353 msi_free(display_name);
6354 return r;
6357 static UINT ACTION_StartServices( MSIPACKAGE *package )
6359 static const WCHAR query[] = {
6360 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6361 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6362 MSIQUERY *view;
6363 UINT rc;
6365 if (package->script == SCRIPT_NONE)
6366 return msi_schedule_action(package, SCRIPT_INSTALL, szStartServices);
6368 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6369 if (rc != ERROR_SUCCESS)
6370 return ERROR_SUCCESS;
6372 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6373 msiobj_release(&view->hdr);
6374 return rc;
6377 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6379 DWORD i, needed, count;
6380 ENUM_SERVICE_STATUSW *dependencies;
6381 SERVICE_STATUS ss;
6382 SC_HANDLE depserv;
6383 BOOL stopped, ret = FALSE;
6385 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6386 0, &needed, &count))
6387 return TRUE;
6389 if (GetLastError() != ERROR_MORE_DATA)
6390 return FALSE;
6392 dependencies = msi_alloc(needed);
6393 if (!dependencies)
6394 return FALSE;
6396 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6397 needed, &needed, &count))
6398 goto done;
6400 for (i = 0; i < count; i++)
6402 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6403 SERVICE_STOP | SERVICE_QUERY_STATUS);
6404 if (!depserv)
6405 goto done;
6407 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6408 CloseServiceHandle(depserv);
6409 if (!stopped)
6410 goto done;
6413 ret = TRUE;
6415 done:
6416 msi_free(dependencies);
6417 return ret;
6420 static UINT stop_service( LPCWSTR name )
6422 SC_HANDLE scm = NULL, service = NULL;
6423 SERVICE_STATUS status;
6424 SERVICE_STATUS_PROCESS ssp;
6425 DWORD needed;
6427 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6428 if (!scm)
6430 WARN("Failed to open the SCM: %d\n", GetLastError());
6431 goto done;
6434 service = OpenServiceW(scm, name,
6435 SERVICE_STOP |
6436 SERVICE_QUERY_STATUS |
6437 SERVICE_ENUMERATE_DEPENDENTS);
6438 if (!service)
6440 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6441 goto done;
6444 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6445 sizeof(SERVICE_STATUS_PROCESS), &needed))
6447 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6448 goto done;
6451 if (ssp.dwCurrentState == SERVICE_STOPPED)
6452 goto done;
6454 stop_service_dependents(scm, service);
6456 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6457 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6459 done:
6460 if (service) CloseServiceHandle(service);
6461 if (scm) CloseServiceHandle(scm);
6463 return ERROR_SUCCESS;
6466 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6468 MSIPACKAGE *package = param;
6469 MSICOMPONENT *comp;
6470 MSIRECORD *uirow;
6471 LPCWSTR component;
6472 WCHAR *name, *display_name = NULL;
6473 DWORD event, len;
6474 SC_HANDLE scm;
6476 component = MSI_RecordGetString( rec, 6 );
6477 comp = msi_get_loaded_component( package, component );
6478 if (!comp)
6479 return ERROR_SUCCESS;
6481 event = MSI_RecordGetInteger( rec, 3 );
6482 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6484 comp->Action = msi_get_component_action( package, comp );
6485 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventStop)) &&
6486 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallStop)))
6488 TRACE("not stopping %s\n", debugstr_w(name));
6489 msi_free( name );
6490 return ERROR_SUCCESS;
6493 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6494 if (!scm)
6496 ERR("Failed to open the service control manager\n");
6497 goto done;
6500 len = 0;
6501 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6502 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6504 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6505 GetServiceDisplayNameW( scm, name, display_name, &len );
6507 CloseServiceHandle( scm );
6509 stop_service( name );
6511 done:
6512 uirow = MSI_CreateRecord( 2 );
6513 MSI_RecordSetStringW( uirow, 1, display_name );
6514 MSI_RecordSetStringW( uirow, 2, name );
6515 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6516 msiobj_release( &uirow->hdr );
6518 msi_free( name );
6519 msi_free( display_name );
6520 return ERROR_SUCCESS;
6523 static UINT ACTION_StopServices( MSIPACKAGE *package )
6525 static const WCHAR query[] = {
6526 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6527 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6528 MSIQUERY *view;
6529 UINT rc;
6531 if (package->script == SCRIPT_NONE)
6532 return msi_schedule_action(package, SCRIPT_INSTALL, szStopServices);
6534 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6535 if (rc != ERROR_SUCCESS)
6536 return ERROR_SUCCESS;
6538 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6539 msiobj_release(&view->hdr);
6540 return rc;
6543 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6545 MSIPACKAGE *package = param;
6546 MSICOMPONENT *comp;
6547 MSIRECORD *uirow;
6548 LPWSTR name = NULL, display_name = NULL;
6549 DWORD event, len;
6550 SC_HANDLE scm = NULL, service = NULL;
6552 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6553 if (!comp)
6554 return ERROR_SUCCESS;
6556 event = MSI_RecordGetInteger( rec, 3 );
6557 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6559 comp->Action = msi_get_component_action( package, comp );
6560 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6561 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6563 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6564 msi_free( name );
6565 return ERROR_SUCCESS;
6567 stop_service( name );
6569 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6570 if (!scm)
6572 WARN("Failed to open the SCM: %d\n", GetLastError());
6573 goto done;
6576 len = 0;
6577 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6578 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6580 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6581 GetServiceDisplayNameW( scm, name, display_name, &len );
6584 service = OpenServiceW( scm, name, DELETE );
6585 if (!service)
6587 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6588 goto done;
6591 if (!DeleteService( service ))
6592 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6594 done:
6595 uirow = MSI_CreateRecord( 2 );
6596 MSI_RecordSetStringW( uirow, 1, display_name );
6597 MSI_RecordSetStringW( uirow, 2, name );
6598 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6599 msiobj_release( &uirow->hdr );
6601 if (service) CloseServiceHandle( service );
6602 if (scm) CloseServiceHandle( scm );
6603 msi_free( name );
6604 msi_free( display_name );
6606 return ERROR_SUCCESS;
6609 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6611 static const WCHAR query[] = {
6612 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6613 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6614 MSIQUERY *view;
6615 UINT rc;
6617 if (package->script == SCRIPT_NONE)
6618 return msi_schedule_action(package, SCRIPT_INSTALL, szDeleteServices);
6620 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6621 if (rc != ERROR_SUCCESS)
6622 return ERROR_SUCCESS;
6624 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6625 msiobj_release( &view->hdr );
6626 return rc;
6629 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6631 MSIPACKAGE *package = param;
6632 LPWSTR driver, driver_path, ptr;
6633 WCHAR outpath[MAX_PATH];
6634 MSIFILE *driver_file = NULL, *setup_file = NULL;
6635 MSICOMPONENT *comp;
6636 MSIRECORD *uirow;
6637 LPCWSTR desc, file_key, component;
6638 DWORD len, usage;
6639 UINT r = ERROR_SUCCESS;
6641 static const WCHAR driver_fmt[] = {
6642 'D','r','i','v','e','r','=','%','s',0};
6643 static const WCHAR setup_fmt[] = {
6644 'S','e','t','u','p','=','%','s',0};
6645 static const WCHAR usage_fmt[] = {
6646 'F','i','l','e','U','s','a','g','e','=','1',0};
6648 component = MSI_RecordGetString( rec, 2 );
6649 comp = msi_get_loaded_component( package, component );
6650 if (!comp)
6651 return ERROR_SUCCESS;
6653 comp->Action = msi_get_component_action( package, comp );
6654 if (comp->Action != INSTALLSTATE_LOCAL)
6656 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6657 return ERROR_SUCCESS;
6659 desc = MSI_RecordGetString(rec, 3);
6661 file_key = MSI_RecordGetString( rec, 4 );
6662 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6664 file_key = MSI_RecordGetString( rec, 5 );
6665 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6667 if (!driver_file)
6669 ERR("ODBC Driver entry not found!\n");
6670 return ERROR_FUNCTION_FAILED;
6673 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6674 if (setup_file)
6675 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6676 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6678 driver = msi_alloc(len * sizeof(WCHAR));
6679 if (!driver)
6680 return ERROR_OUTOFMEMORY;
6682 ptr = driver;
6683 lstrcpyW(ptr, desc);
6684 ptr += lstrlenW(ptr) + 1;
6686 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6687 ptr += len + 1;
6689 if (setup_file)
6691 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6692 ptr += len + 1;
6695 lstrcpyW(ptr, usage_fmt);
6696 ptr += lstrlenW(ptr) + 1;
6697 *ptr = '\0';
6699 if (!driver_file->TargetPath)
6701 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6702 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6704 driver_path = strdupW(driver_file->TargetPath);
6705 ptr = strrchrW(driver_path, '\\');
6706 if (ptr) *ptr = '\0';
6708 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6709 NULL, ODBC_INSTALL_COMPLETE, &usage))
6711 ERR("Failed to install SQL driver!\n");
6712 r = ERROR_FUNCTION_FAILED;
6715 uirow = MSI_CreateRecord( 5 );
6716 MSI_RecordSetStringW( uirow, 1, desc );
6717 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6718 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6719 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6720 msiobj_release( &uirow->hdr );
6722 msi_free(driver);
6723 msi_free(driver_path);
6725 return r;
6728 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6730 MSIPACKAGE *package = param;
6731 LPWSTR translator, translator_path, ptr;
6732 WCHAR outpath[MAX_PATH];
6733 MSIFILE *translator_file = NULL, *setup_file = NULL;
6734 MSICOMPONENT *comp;
6735 MSIRECORD *uirow;
6736 LPCWSTR desc, file_key, component;
6737 DWORD len, usage;
6738 UINT r = ERROR_SUCCESS;
6740 static const WCHAR translator_fmt[] = {
6741 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6742 static const WCHAR setup_fmt[] = {
6743 'S','e','t','u','p','=','%','s',0};
6745 component = MSI_RecordGetString( rec, 2 );
6746 comp = msi_get_loaded_component( package, component );
6747 if (!comp)
6748 return ERROR_SUCCESS;
6750 comp->Action = msi_get_component_action( package, comp );
6751 if (comp->Action != INSTALLSTATE_LOCAL)
6753 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6754 return ERROR_SUCCESS;
6756 desc = MSI_RecordGetString(rec, 3);
6758 file_key = MSI_RecordGetString( rec, 4 );
6759 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6761 file_key = MSI_RecordGetString( rec, 5 );
6762 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6764 if (!translator_file)
6766 ERR("ODBC Translator entry not found!\n");
6767 return ERROR_FUNCTION_FAILED;
6770 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6771 if (setup_file)
6772 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6774 translator = msi_alloc(len * sizeof(WCHAR));
6775 if (!translator)
6776 return ERROR_OUTOFMEMORY;
6778 ptr = translator;
6779 lstrcpyW(ptr, desc);
6780 ptr += lstrlenW(ptr) + 1;
6782 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6783 ptr += len + 1;
6785 if (setup_file)
6787 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6788 ptr += len + 1;
6790 *ptr = '\0';
6792 translator_path = strdupW(translator_file->TargetPath);
6793 ptr = strrchrW(translator_path, '\\');
6794 if (ptr) *ptr = '\0';
6796 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6797 NULL, ODBC_INSTALL_COMPLETE, &usage))
6799 ERR("Failed to install SQL translator!\n");
6800 r = ERROR_FUNCTION_FAILED;
6803 uirow = MSI_CreateRecord( 5 );
6804 MSI_RecordSetStringW( uirow, 1, desc );
6805 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6806 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6807 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6808 msiobj_release( &uirow->hdr );
6810 msi_free(translator);
6811 msi_free(translator_path);
6813 return r;
6816 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6818 MSIPACKAGE *package = param;
6819 MSICOMPONENT *comp;
6820 LPWSTR attrs;
6821 LPCWSTR desc, driver, component;
6822 WORD request = ODBC_ADD_SYS_DSN;
6823 INT registration;
6824 DWORD len;
6825 UINT r = ERROR_SUCCESS;
6826 MSIRECORD *uirow;
6828 static const WCHAR attrs_fmt[] = {
6829 'D','S','N','=','%','s',0 };
6831 component = MSI_RecordGetString( rec, 2 );
6832 comp = msi_get_loaded_component( package, component );
6833 if (!comp)
6834 return ERROR_SUCCESS;
6836 comp->Action = msi_get_component_action( package, comp );
6837 if (comp->Action != INSTALLSTATE_LOCAL)
6839 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6840 return ERROR_SUCCESS;
6843 desc = MSI_RecordGetString(rec, 3);
6844 driver = MSI_RecordGetString(rec, 4);
6845 registration = MSI_RecordGetInteger(rec, 5);
6847 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6848 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6850 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6851 attrs = msi_alloc(len * sizeof(WCHAR));
6852 if (!attrs)
6853 return ERROR_OUTOFMEMORY;
6855 len = sprintfW(attrs, attrs_fmt, desc);
6856 attrs[len + 1] = 0;
6858 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6860 ERR("Failed to install SQL data source!\n");
6861 r = ERROR_FUNCTION_FAILED;
6864 uirow = MSI_CreateRecord( 5 );
6865 MSI_RecordSetStringW( uirow, 1, desc );
6866 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6867 MSI_RecordSetInteger( uirow, 3, request );
6868 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6869 msiobj_release( &uirow->hdr );
6871 msi_free(attrs);
6873 return r;
6876 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6878 static const WCHAR driver_query[] = {
6879 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6880 'O','D','B','C','D','r','i','v','e','r',0};
6881 static const WCHAR translator_query[] = {
6882 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6883 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6884 static const WCHAR source_query[] = {
6885 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6886 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6887 MSIQUERY *view;
6888 UINT rc;
6890 if (package->script == SCRIPT_NONE)
6891 return msi_schedule_action(package, SCRIPT_INSTALL, szInstallODBC);
6893 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6894 if (rc == ERROR_SUCCESS)
6896 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6897 msiobj_release(&view->hdr);
6898 if (rc != ERROR_SUCCESS)
6899 return rc;
6901 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6902 if (rc == ERROR_SUCCESS)
6904 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6905 msiobj_release(&view->hdr);
6906 if (rc != ERROR_SUCCESS)
6907 return rc;
6909 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6910 if (rc == ERROR_SUCCESS)
6912 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6913 msiobj_release(&view->hdr);
6914 if (rc != ERROR_SUCCESS)
6915 return rc;
6917 return ERROR_SUCCESS;
6920 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6922 MSIPACKAGE *package = param;
6923 MSICOMPONENT *comp;
6924 MSIRECORD *uirow;
6925 DWORD usage;
6926 LPCWSTR desc, component;
6928 component = MSI_RecordGetString( rec, 2 );
6929 comp = msi_get_loaded_component( package, component );
6930 if (!comp)
6931 return ERROR_SUCCESS;
6933 comp->Action = msi_get_component_action( package, comp );
6934 if (comp->Action != INSTALLSTATE_ABSENT)
6936 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6937 return ERROR_SUCCESS;
6940 desc = MSI_RecordGetString( rec, 3 );
6941 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6943 WARN("Failed to remove ODBC driver\n");
6945 else if (!usage)
6947 FIXME("Usage count reached 0\n");
6950 uirow = MSI_CreateRecord( 2 );
6951 MSI_RecordSetStringW( uirow, 1, desc );
6952 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6953 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6954 msiobj_release( &uirow->hdr );
6956 return ERROR_SUCCESS;
6959 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6961 MSIPACKAGE *package = param;
6962 MSICOMPONENT *comp;
6963 MSIRECORD *uirow;
6964 DWORD usage;
6965 LPCWSTR desc, component;
6967 component = MSI_RecordGetString( rec, 2 );
6968 comp = msi_get_loaded_component( package, component );
6969 if (!comp)
6970 return ERROR_SUCCESS;
6972 comp->Action = msi_get_component_action( package, comp );
6973 if (comp->Action != INSTALLSTATE_ABSENT)
6975 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6976 return ERROR_SUCCESS;
6979 desc = MSI_RecordGetString( rec, 3 );
6980 if (!SQLRemoveTranslatorW( desc, &usage ))
6982 WARN("Failed to remove ODBC translator\n");
6984 else if (!usage)
6986 FIXME("Usage count reached 0\n");
6989 uirow = MSI_CreateRecord( 2 );
6990 MSI_RecordSetStringW( uirow, 1, desc );
6991 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6992 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
6993 msiobj_release( &uirow->hdr );
6995 return ERROR_SUCCESS;
6998 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
7000 MSIPACKAGE *package = param;
7001 MSICOMPONENT *comp;
7002 MSIRECORD *uirow;
7003 LPWSTR attrs;
7004 LPCWSTR desc, driver, component;
7005 WORD request = ODBC_REMOVE_SYS_DSN;
7006 INT registration;
7007 DWORD len;
7009 static const WCHAR attrs_fmt[] = {
7010 'D','S','N','=','%','s',0 };
7012 component = MSI_RecordGetString( rec, 2 );
7013 comp = msi_get_loaded_component( package, component );
7014 if (!comp)
7015 return ERROR_SUCCESS;
7017 comp->Action = msi_get_component_action( package, comp );
7018 if (comp->Action != INSTALLSTATE_ABSENT)
7020 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7021 return ERROR_SUCCESS;
7024 desc = MSI_RecordGetString( rec, 3 );
7025 driver = MSI_RecordGetString( rec, 4 );
7026 registration = MSI_RecordGetInteger( rec, 5 );
7028 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
7029 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
7031 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
7032 attrs = msi_alloc( len * sizeof(WCHAR) );
7033 if (!attrs)
7034 return ERROR_OUTOFMEMORY;
7036 FIXME("Use ODBCSourceAttribute table\n");
7038 len = sprintfW( attrs, attrs_fmt, desc );
7039 attrs[len + 1] = 0;
7041 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
7043 WARN("Failed to remove ODBC data source\n");
7045 msi_free( attrs );
7047 uirow = MSI_CreateRecord( 3 );
7048 MSI_RecordSetStringW( uirow, 1, desc );
7049 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
7050 MSI_RecordSetInteger( uirow, 3, request );
7051 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7052 msiobj_release( &uirow->hdr );
7054 return ERROR_SUCCESS;
7057 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
7059 static const WCHAR driver_query[] = {
7060 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7061 'O','D','B','C','D','r','i','v','e','r',0};
7062 static const WCHAR translator_query[] = {
7063 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7064 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7065 static const WCHAR source_query[] = {
7066 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7067 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
7068 MSIQUERY *view;
7069 UINT rc;
7071 if (package->script == SCRIPT_NONE)
7072 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveODBC);
7074 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7075 if (rc == ERROR_SUCCESS)
7077 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
7078 msiobj_release( &view->hdr );
7079 if (rc != ERROR_SUCCESS)
7080 return rc;
7082 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7083 if (rc == ERROR_SUCCESS)
7085 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
7086 msiobj_release( &view->hdr );
7087 if (rc != ERROR_SUCCESS)
7088 return rc;
7090 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
7091 if (rc == ERROR_SUCCESS)
7093 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
7094 msiobj_release( &view->hdr );
7095 if (rc != ERROR_SUCCESS)
7096 return rc;
7098 return ERROR_SUCCESS;
7101 #define ENV_ACT_SETALWAYS 0x1
7102 #define ENV_ACT_SETABSENT 0x2
7103 #define ENV_ACT_REMOVE 0x4
7104 #define ENV_ACT_REMOVEMATCH 0x8
7106 #define ENV_MOD_MACHINE 0x20000000
7107 #define ENV_MOD_APPEND 0x40000000
7108 #define ENV_MOD_PREFIX 0x80000000
7109 #define ENV_MOD_MASK 0xC0000000
7111 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
7113 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
7115 LPCWSTR cptr = *name;
7117 static const WCHAR prefix[] = {'[','~',']',0};
7118 static const int prefix_len = 3;
7120 *flags = 0;
7121 while (*cptr)
7123 if (*cptr == '=')
7124 *flags |= ENV_ACT_SETALWAYS;
7125 else if (*cptr == '+')
7126 *flags |= ENV_ACT_SETABSENT;
7127 else if (*cptr == '-')
7128 *flags |= ENV_ACT_REMOVE;
7129 else if (*cptr == '!')
7130 *flags |= ENV_ACT_REMOVEMATCH;
7131 else if (*cptr == '*')
7132 *flags |= ENV_MOD_MACHINE;
7133 else
7134 break;
7136 cptr++;
7137 (*name)++;
7140 if (!*cptr)
7142 ERR("Missing environment variable\n");
7143 return ERROR_FUNCTION_FAILED;
7146 if (*value)
7148 LPCWSTR ptr = *value;
7149 if (!strncmpW(ptr, prefix, prefix_len))
7151 if (ptr[prefix_len] == szSemiColon[0])
7153 *flags |= ENV_MOD_APPEND;
7154 *value += lstrlenW(prefix);
7156 else
7158 *value = NULL;
7161 else if (lstrlenW(*value) >= prefix_len)
7163 ptr += lstrlenW(ptr) - prefix_len;
7164 if (!strcmpW( ptr, prefix ))
7166 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
7168 *flags |= ENV_MOD_PREFIX;
7169 /* the "[~]" will be removed by deformat_string */;
7171 else
7173 *value = NULL;
7179 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
7180 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
7181 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
7182 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
7184 ERR("Invalid flags: %08x\n", *flags);
7185 return ERROR_FUNCTION_FAILED;
7188 if (!*flags)
7189 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
7191 return ERROR_SUCCESS;
7194 static UINT open_env_key( DWORD flags, HKEY *key )
7196 static const WCHAR user_env[] =
7197 {'E','n','v','i','r','o','n','m','e','n','t',0};
7198 static const WCHAR machine_env[] =
7199 {'S','y','s','t','e','m','\\',
7200 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
7201 'C','o','n','t','r','o','l','\\',
7202 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
7203 'E','n','v','i','r','o','n','m','e','n','t',0};
7204 const WCHAR *env;
7205 HKEY root;
7206 LONG res;
7208 if (flags & ENV_MOD_MACHINE)
7210 env = machine_env;
7211 root = HKEY_LOCAL_MACHINE;
7213 else
7215 env = user_env;
7216 root = HKEY_CURRENT_USER;
7219 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
7220 if (res != ERROR_SUCCESS)
7222 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
7223 return ERROR_FUNCTION_FAILED;
7226 return ERROR_SUCCESS;
7229 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
7231 MSIPACKAGE *package = param;
7232 LPCWSTR name, value, component;
7233 WCHAR *data = NULL, *newval = NULL, *deformatted = NULL, *p, *q;
7234 DWORD flags, type, size, len, len_value = 0;
7235 UINT res;
7236 HKEY env = NULL;
7237 MSICOMPONENT *comp;
7238 MSIRECORD *uirow;
7239 int action = 0, found = 0;
7241 component = MSI_RecordGetString(rec, 4);
7242 comp = msi_get_loaded_component(package, component);
7243 if (!comp)
7244 return ERROR_SUCCESS;
7246 comp->Action = msi_get_component_action( package, comp );
7247 if (comp->Action != INSTALLSTATE_LOCAL)
7249 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
7250 return ERROR_SUCCESS;
7252 name = MSI_RecordGetString(rec, 2);
7253 value = MSI_RecordGetString(rec, 3);
7255 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7257 res = env_parse_flags(&name, &value, &flags);
7258 if (res != ERROR_SUCCESS || !value)
7259 goto done;
7261 if (value && !deformat_string(package, value, &deformatted))
7263 res = ERROR_OUTOFMEMORY;
7264 goto done;
7267 if ((value = deformatted))
7269 if (flags & ENV_MOD_PREFIX)
7271 p = strrchrW( value, ';' );
7272 len_value = p - value;
7274 else if (flags & ENV_MOD_APPEND)
7276 value = strchrW( value, ';' ) + 1;
7277 len_value = strlenW( value );
7279 else len_value = strlenW( value );
7282 res = open_env_key( flags, &env );
7283 if (res != ERROR_SUCCESS)
7284 goto done;
7286 if (flags & ENV_MOD_MACHINE)
7287 action |= 0x20000000;
7289 size = 0;
7290 type = REG_SZ;
7291 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
7292 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
7293 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
7294 goto done;
7296 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
7298 action = 0x2;
7300 /* Nothing to do. */
7301 if (!value)
7303 res = ERROR_SUCCESS;
7304 goto done;
7306 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
7307 newval = strdupW(value);
7308 if (!newval)
7310 res = ERROR_OUTOFMEMORY;
7311 goto done;
7314 else
7316 action = 0x1;
7318 /* Contrary to MSDN, +-variable to [~];path works */
7319 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
7321 res = ERROR_SUCCESS;
7322 goto done;
7325 if (!(p = q = data = msi_alloc( size )))
7327 msi_free(deformatted);
7328 RegCloseKey(env);
7329 return ERROR_OUTOFMEMORY;
7332 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)data, &size );
7333 if (res != ERROR_SUCCESS)
7334 goto done;
7336 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7338 action = 0x4;
7339 res = RegDeleteValueW(env, name);
7340 if (res != ERROR_SUCCESS)
7341 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7342 goto done;
7345 for (;;)
7347 while (*q && *q != ';') q++;
7348 len = q - p;
7349 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ) &&
7350 (!p[len] || p[len] == ';'))
7352 found = 1;
7353 break;
7355 if (!*q) break;
7356 p = ++q;
7359 if (found)
7361 TRACE("string already set\n");
7362 goto done;
7365 size = (len_value + 1 + strlenW( data ) + 1) * sizeof(WCHAR);
7366 if (!(p = newval = msi_alloc( size )))
7368 res = ERROR_OUTOFMEMORY;
7369 goto done;
7372 if (flags & ENV_MOD_PREFIX)
7374 memcpy( newval, value, len_value * sizeof(WCHAR) );
7375 newval[len_value] = ';';
7376 p = newval + len_value + 1;
7377 action |= 0x80000000;
7380 strcpyW( p, data );
7382 if (flags & ENV_MOD_APPEND)
7384 p += strlenW( data );
7385 *p++ = ';';
7386 memcpy( p, value, (len_value + 1) * sizeof(WCHAR) );
7387 action |= 0x40000000;
7390 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7391 res = RegSetValueExW( env, name, 0, type, (BYTE *)newval, size );
7392 if (res)
7394 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7397 done:
7398 uirow = MSI_CreateRecord( 3 );
7399 MSI_RecordSetStringW( uirow, 1, name );
7400 MSI_RecordSetStringW( uirow, 2, newval );
7401 MSI_RecordSetInteger( uirow, 3, action );
7402 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7403 msiobj_release( &uirow->hdr );
7405 if (env) RegCloseKey(env);
7406 msi_free(deformatted);
7407 msi_free(data);
7408 msi_free(newval);
7409 return res;
7412 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7414 static const WCHAR query[] = {
7415 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7416 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7417 MSIQUERY *view;
7418 UINT rc;
7420 if (package->script == SCRIPT_NONE)
7421 return msi_schedule_action(package, SCRIPT_INSTALL, szWriteEnvironmentStrings);
7423 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7424 if (rc != ERROR_SUCCESS)
7425 return ERROR_SUCCESS;
7427 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7428 msiobj_release(&view->hdr);
7429 return rc;
7432 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7434 MSIPACKAGE *package = param;
7435 LPCWSTR name, value, component;
7436 WCHAR *p, *q, *deformatted = NULL, *new_value = NULL;
7437 DWORD flags, type, size, len, len_value = 0, len_new_value;
7438 HKEY env;
7439 MSICOMPONENT *comp;
7440 MSIRECORD *uirow;
7441 int action = 0;
7442 LONG res;
7443 UINT r;
7445 component = MSI_RecordGetString( rec, 4 );
7446 comp = msi_get_loaded_component( package, component );
7447 if (!comp)
7448 return ERROR_SUCCESS;
7450 comp->Action = msi_get_component_action( package, comp );
7451 if (comp->Action != INSTALLSTATE_ABSENT)
7453 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7454 return ERROR_SUCCESS;
7456 name = MSI_RecordGetString( rec, 2 );
7457 value = MSI_RecordGetString( rec, 3 );
7459 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7461 r = env_parse_flags( &name, &value, &flags );
7462 if (r != ERROR_SUCCESS)
7463 return r;
7465 if (!(flags & ENV_ACT_REMOVE))
7467 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7468 return ERROR_SUCCESS;
7471 if (value && !deformat_string( package, value, &deformatted ))
7472 return ERROR_OUTOFMEMORY;
7474 if ((value = deformatted))
7476 if (flags & ENV_MOD_PREFIX)
7478 p = strchrW( value, ';' );
7479 len_value = p - value;
7481 else if (flags & ENV_MOD_APPEND)
7483 value = strchrW( value, ';' ) + 1;
7484 len_value = strlenW( value );
7486 else len_value = strlenW( value );
7489 r = open_env_key( flags, &env );
7490 if (r != ERROR_SUCCESS)
7492 r = ERROR_SUCCESS;
7493 goto done;
7496 if (flags & ENV_MOD_MACHINE)
7497 action |= 0x20000000;
7499 size = 0;
7500 type = REG_SZ;
7501 res = RegQueryValueExW( env, name, NULL, &type, NULL, &size );
7502 if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ))
7503 goto done;
7505 if (!(new_value = msi_alloc( size ))) goto done;
7507 res = RegQueryValueExW( env, name, NULL, &type, (BYTE *)new_value, &size );
7508 if (res != ERROR_SUCCESS)
7509 goto done;
7511 len_new_value = size / sizeof(WCHAR) - 1;
7512 p = q = new_value;
7513 for (;;)
7515 while (*q && *q != ';') q++;
7516 len = q - p;
7517 if (value && len == len_value && !memcmp( value, p, len * sizeof(WCHAR) ))
7519 if (*q == ';') q++;
7520 memmove( p, q, (len_new_value - (q - new_value) + 1) * sizeof(WCHAR) );
7521 break;
7523 if (!*q) break;
7524 p = ++q;
7527 if (!new_value[0] || !value)
7529 TRACE("removing %s\n", debugstr_w(name));
7530 res = RegDeleteValueW( env, name );
7531 if (res != ERROR_SUCCESS)
7532 WARN("failed to delete value %s (%d)\n", debugstr_w(name), res);
7534 else
7536 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(new_value));
7537 size = (strlenW( new_value ) + 1) * sizeof(WCHAR);
7538 res = RegSetValueExW( env, name, 0, type, (BYTE *)new_value, size );
7539 if (res != ERROR_SUCCESS)
7540 WARN("failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(new_value), res);
7543 done:
7544 uirow = MSI_CreateRecord( 3 );
7545 MSI_RecordSetStringW( uirow, 1, name );
7546 MSI_RecordSetStringW( uirow, 2, value );
7547 MSI_RecordSetInteger( uirow, 3, action );
7548 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7549 msiobj_release( &uirow->hdr );
7551 if (env) RegCloseKey( env );
7552 msi_free( deformatted );
7553 msi_free( new_value );
7554 return r;
7557 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7559 static const WCHAR query[] = {
7560 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7561 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7562 MSIQUERY *view;
7563 UINT rc;
7565 if (package->script == SCRIPT_NONE)
7566 return msi_schedule_action(package, SCRIPT_INSTALL, szRemoveEnvironmentStrings);
7568 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7569 if (rc != ERROR_SUCCESS)
7570 return ERROR_SUCCESS;
7572 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7573 msiobj_release( &view->hdr );
7574 return rc;
7577 UINT msi_validate_product_id( MSIPACKAGE *package )
7579 LPWSTR key, template, id;
7580 UINT r = ERROR_SUCCESS;
7582 id = msi_dup_property( package->db, szProductID );
7583 if (id)
7585 msi_free( id );
7586 return ERROR_SUCCESS;
7588 template = msi_dup_property( package->db, szPIDTemplate );
7589 key = msi_dup_property( package->db, szPIDKEY );
7590 if (key && template)
7592 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7593 r = msi_set_property( package->db, szProductID, key, -1 );
7595 msi_free( template );
7596 msi_free( key );
7597 return r;
7600 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7602 return msi_validate_product_id( package );
7605 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7607 TRACE("\n");
7608 package->need_reboot_at_end = 1;
7609 return ERROR_SUCCESS;
7612 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7614 static const WCHAR szAvailableFreeReg[] =
7615 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7616 MSIRECORD *uirow;
7617 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7619 TRACE("%p %d kilobytes\n", package, space);
7621 uirow = MSI_CreateRecord( 1 );
7622 MSI_RecordSetInteger( uirow, 1, space );
7623 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
7624 msiobj_release( &uirow->hdr );
7626 return ERROR_SUCCESS;
7629 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7631 TRACE("%p\n", package);
7633 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7634 return ERROR_SUCCESS;
7637 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7639 FIXME("%p\n", package);
7640 return ERROR_SUCCESS;
7643 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7645 static const WCHAR driver_query[] = {
7646 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7647 'O','D','B','C','D','r','i','v','e','r',0};
7648 static const WCHAR translator_query[] = {
7649 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7650 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7651 MSIQUERY *view;
7652 UINT r, count;
7654 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7655 if (r == ERROR_SUCCESS)
7657 count = 0;
7658 r = MSI_IterateRecords( view, &count, NULL, package );
7659 msiobj_release( &view->hdr );
7660 if (r != ERROR_SUCCESS)
7661 return r;
7662 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7664 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7665 if (r == ERROR_SUCCESS)
7667 count = 0;
7668 r = MSI_IterateRecords( view, &count, NULL, package );
7669 msiobj_release( &view->hdr );
7670 if (r != ERROR_SUCCESS)
7671 return r;
7672 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7674 return ERROR_SUCCESS;
7677 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7679 static const WCHAR fmtW[] =
7680 {'m','s','i','e','x','e','c',' ','/','q','n',' ','/','i',' ','%','s',' ',
7681 'R','E','M','O','V','E','=','%','s',0};
7682 MSIPACKAGE *package = param;
7683 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7684 int attrs = MSI_RecordGetInteger( rec, 5 );
7685 UINT len = ARRAY_SIZE( fmtW );
7686 WCHAR *product, *features, *cmd;
7687 STARTUPINFOW si;
7688 PROCESS_INFORMATION info;
7689 BOOL ret;
7691 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7692 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7694 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7696 len += strlenW( product );
7697 if (features)
7698 len += strlenW( features );
7699 else
7700 len += ARRAY_SIZE( szAll );
7702 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7704 msi_free( product );
7705 msi_free( features );
7706 return ERROR_OUTOFMEMORY;
7708 sprintfW( cmd, fmtW, product, features ? features : szAll );
7709 msi_free( product );
7710 msi_free( features );
7712 memset( &si, 0, sizeof(STARTUPINFOW) );
7713 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7714 msi_free( cmd );
7715 if (!ret) return GetLastError();
7716 CloseHandle( info.hThread );
7718 WaitForSingleObject( info.hProcess, INFINITE );
7719 CloseHandle( info.hProcess );
7720 return ERROR_SUCCESS;
7723 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7725 static const WCHAR query[] = {
7726 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7727 MSIQUERY *view;
7728 UINT r;
7730 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7731 if (r == ERROR_SUCCESS)
7733 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7734 msiobj_release( &view->hdr );
7735 if (r != ERROR_SUCCESS)
7736 return r;
7738 return ERROR_SUCCESS;
7741 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7743 MSIPACKAGE *package = param;
7744 int attributes = MSI_RecordGetInteger( rec, 5 );
7746 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7748 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7749 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7750 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7751 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7752 HKEY hkey;
7753 UINT r;
7755 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7757 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7758 if (r != ERROR_SUCCESS)
7759 return ERROR_SUCCESS;
7761 else
7763 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7764 if (r != ERROR_SUCCESS)
7765 return ERROR_SUCCESS;
7767 RegCloseKey( hkey );
7769 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7770 debugstr_w(upgrade_code), debugstr_w(version_min),
7771 debugstr_w(version_max), debugstr_w(language));
7773 return ERROR_SUCCESS;
7776 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7778 static const WCHAR query[] = {
7779 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7780 'U','p','g','r','a','d','e',0};
7781 MSIQUERY *view;
7782 UINT r;
7784 if (msi_get_property_int( package->db, szInstalled, 0 ))
7786 TRACE("product is installed, skipping action\n");
7787 return ERROR_SUCCESS;
7789 if (msi_get_property_int( package->db, szPreselected, 0 ))
7791 TRACE("Preselected property is set, not migrating feature states\n");
7792 return ERROR_SUCCESS;
7794 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7795 if (r == ERROR_SUCCESS)
7797 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7798 msiobj_release( &view->hdr );
7799 if (r != ERROR_SUCCESS)
7800 return r;
7802 return ERROR_SUCCESS;
7805 static void bind_image( const char *filename, const char *path )
7807 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7809 WARN("failed to bind image %u\n", GetLastError());
7813 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7815 UINT i;
7816 MSIFILE *file;
7817 MSIPACKAGE *package = param;
7818 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7819 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7820 char *filenameA, *pathA;
7821 WCHAR *pathW, **path_list;
7823 if (!(file = msi_get_loaded_file( package, key )))
7825 WARN("file %s not found\n", debugstr_w(key));
7826 return ERROR_SUCCESS;
7828 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7829 path_list = msi_split_string( paths, ';' );
7830 if (!path_list) bind_image( filenameA, NULL );
7831 else
7833 for (i = 0; path_list[i] && path_list[i][0]; i++)
7835 deformat_string( package, path_list[i], &pathW );
7836 if ((pathA = strdupWtoA( pathW )))
7838 bind_image( filenameA, pathA );
7839 msi_free( pathA );
7841 msi_free( pathW );
7844 msi_free( path_list );
7845 msi_free( filenameA );
7846 return ERROR_SUCCESS;
7849 static UINT ACTION_BindImage( MSIPACKAGE *package )
7851 static const WCHAR query[] = {
7852 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7853 'B','i','n','d','I','m','a','g','e',0};
7854 MSIQUERY *view;
7855 UINT r;
7857 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7858 if (r == ERROR_SUCCESS)
7860 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7861 msiobj_release( &view->hdr );
7862 if (r != ERROR_SUCCESS)
7863 return r;
7865 return ERROR_SUCCESS;
7868 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7870 static const WCHAR query[] = {
7871 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7872 MSIQUERY *view;
7873 DWORD count = 0;
7874 UINT r;
7876 r = MSI_OpenQuery( package->db, &view, query, table );
7877 if (r == ERROR_SUCCESS)
7879 r = MSI_IterateRecords(view, &count, NULL, package);
7880 msiobj_release(&view->hdr);
7881 if (r != ERROR_SUCCESS)
7882 return r;
7884 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7885 return ERROR_SUCCESS;
7888 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7890 static const WCHAR table[] = {
7891 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7892 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7895 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7897 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7898 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7901 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7903 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7904 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7907 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7909 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7910 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7913 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7915 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7916 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7919 static const struct
7921 const WCHAR *action;
7922 const UINT description;
7923 const UINT template;
7924 UINT (*handler)(MSIPACKAGE *);
7925 const WCHAR *action_rollback;
7927 StandardActions[] =
7929 { szAllocateRegistrySpace, IDS_DESC_ALLOCATEREGISTRYSPACE, IDS_TEMP_ALLOCATEREGISTRYSPACE, ACTION_AllocateRegistrySpace, NULL },
7930 { szAppSearch, IDS_DESC_APPSEARCH, IDS_TEMP_APPSEARCH, ACTION_AppSearch, NULL },
7931 { szBindImage, IDS_DESC_BINDIMAGE, IDS_TEMP_BINDIMAGE, ACTION_BindImage, NULL },
7932 { szCCPSearch, IDS_DESC_CCPSEARCH, 0, ACTION_CCPSearch, NULL },
7933 { szCostFinalize, IDS_DESC_COSTFINALIZE, 0, ACTION_CostFinalize, NULL },
7934 { szCostInitialize, IDS_DESC_COSTINITIALIZE, 0, ACTION_CostInitialize, NULL },
7935 { szCreateFolders, IDS_DESC_CREATEFOLDERS, IDS_TEMP_CREATEFOLDERS, ACTION_CreateFolders, szRemoveFolders },
7936 { szCreateShortcuts, IDS_DESC_CREATESHORTCUTS, IDS_TEMP_CREATESHORTCUTS, ACTION_CreateShortcuts, szRemoveShortcuts },
7937 { szDeleteServices, IDS_DESC_DELETESERVICES, IDS_TEMP_DELETESERVICES, ACTION_DeleteServices, szInstallServices },
7938 { szDisableRollback, 0, 0, ACTION_DisableRollback, NULL },
7939 { szDuplicateFiles, IDS_DESC_DUPLICATEFILES, IDS_TEMP_DUPLICATEFILES, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7940 { szExecuteAction, 0, 0, ACTION_ExecuteAction, NULL },
7941 { szFileCost, IDS_DESC_FILECOST, 0, ACTION_FileCost, NULL },
7942 { szFindRelatedProducts, IDS_DESC_FINDRELATEDPRODUCTS, IDS_TEMP_FINDRELATEDPRODUCTS, ACTION_FindRelatedProducts, NULL },
7943 { szForceReboot, 0, 0, ACTION_ForceReboot, NULL },
7944 { szInstallAdminPackage, IDS_DESC_INSTALLADMINPACKAGE, IDS_TEMP_INSTALLADMINPACKAGE, ACTION_InstallAdminPackage, NULL },
7945 { szInstallExecute, 0, 0, ACTION_InstallExecute, NULL },
7946 { szInstallExecuteAgain, 0, 0, ACTION_InstallExecute, NULL },
7947 { szInstallFiles, IDS_DESC_INSTALLFILES, IDS_TEMP_INSTALLFILES, ACTION_InstallFiles, szRemoveFiles },
7948 { szInstallFinalize, 0, 0, ACTION_InstallFinalize, NULL },
7949 { szInstallInitialize, 0, 0, ACTION_InstallInitialize, NULL },
7950 { szInstallODBC, IDS_DESC_INSTALLODBC, 0, ACTION_InstallODBC, szRemoveODBC },
7951 { szInstallServices, IDS_DESC_INSTALLSERVICES, IDS_TEMP_INSTALLSERVICES, ACTION_InstallServices, szDeleteServices },
7952 { szInstallSFPCatalogFile, IDS_DESC_INSTALLSFPCATALOGFILE, IDS_TEMP_INSTALLSFPCATALOGFILE, ACTION_InstallSFPCatalogFile, NULL },
7953 { szInstallValidate, IDS_DESC_INSTALLVALIDATE, 0, ACTION_InstallValidate, NULL },
7954 { szIsolateComponents, 0, 0, ACTION_IsolateComponents, NULL },
7955 { szLaunchConditions, IDS_DESC_LAUNCHCONDITIONS, 0, ACTION_LaunchConditions, NULL },
7956 { szMigrateFeatureStates, IDS_DESC_MIGRATEFEATURESTATES, IDS_TEMP_MIGRATEFEATURESTATES, ACTION_MigrateFeatureStates, NULL },
7957 { szMoveFiles, IDS_DESC_MOVEFILES, IDS_TEMP_MOVEFILES, ACTION_MoveFiles, NULL },
7958 { szMsiPublishAssemblies, IDS_DESC_MSIPUBLISHASSEMBLIES, IDS_TEMP_MSIPUBLISHASSEMBLIES, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7959 { szMsiUnpublishAssemblies, IDS_DESC_MSIUNPUBLISHASSEMBLIES, IDS_TEMP_MSIUNPUBLISHASSEMBLIES, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7960 { szPatchFiles, IDS_DESC_PATCHFILES, IDS_TEMP_PATCHFILES, ACTION_PatchFiles, NULL },
7961 { szProcessComponents, IDS_DESC_PROCESSCOMPONENTS, 0, ACTION_ProcessComponents, szProcessComponents },
7962 { szPublishComponents, IDS_DESC_PUBLISHCOMPONENTS, IDS_TEMP_PUBLISHCOMPONENTS, ACTION_PublishComponents, szUnpublishComponents },
7963 { szPublishFeatures, IDS_DESC_PUBLISHFEATURES, IDS_TEMP_PUBLISHFEATURES, ACTION_PublishFeatures, szUnpublishFeatures },
7964 { szPublishProduct, IDS_DESC_PUBLISHPRODUCT, 0, ACTION_PublishProduct, szUnpublishProduct },
7965 { szRegisterClassInfo, IDS_DESC_REGISTERCLASSINFO, IDS_TEMP_REGISTERCLASSINFO, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7966 { szRegisterComPlus, IDS_DESC_REGISTERCOMPLUS, IDS_TEMP_REGISTERCOMPLUS, ACTION_RegisterComPlus, szUnregisterComPlus },
7967 { szRegisterExtensionInfo, IDS_DESC_REGISTEREXTENSIONINFO, 0, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7968 { szRegisterFonts, IDS_DESC_REGISTERFONTS, IDS_TEMP_REGISTERFONTS, ACTION_RegisterFonts, szUnregisterFonts },
7969 { szRegisterMIMEInfo, IDS_DESC_REGISTERMIMEINFO, IDS_TEMP_REGISTERMIMEINFO, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7970 { szRegisterProduct, IDS_DESC_REGISTERPRODUCT, 0, ACTION_RegisterProduct, NULL },
7971 { szRegisterProgIdInfo, IDS_DESC_REGISTERPROGIDINFO, IDS_TEMP_REGISTERPROGIDINFO, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7972 { szRegisterTypeLibraries, IDS_DESC_REGISTERTYPELIBRARIES, IDS_TEMP_REGISTERTYPELIBRARIES, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7973 { szRegisterUser, IDS_DESC_REGISTERUSER, 0, ACTION_RegisterUser, NULL },
7974 { szRemoveDuplicateFiles, IDS_DESC_REMOVEDUPLICATEFILES, IDS_TEMP_REMOVEDUPLICATEFILES, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7975 { szRemoveEnvironmentStrings, IDS_DESC_REMOVEENVIRONMENTSTRINGS, IDS_TEMP_REMOVEENVIRONMENTSTRINGS, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7976 { szRemoveExistingProducts, IDS_DESC_REMOVEEXISTINGPRODUCTS, IDS_TEMP_REMOVEEXISTINGPRODUCTS, ACTION_RemoveExistingProducts, NULL },
7977 { szRemoveFiles, IDS_DESC_REMOVEFILES, IDS_TEMP_REMOVEFILES, ACTION_RemoveFiles, szInstallFiles },
7978 { szRemoveFolders, IDS_DESC_REMOVEFOLDERS, IDS_TEMP_REMOVEFOLDERS, ACTION_RemoveFolders, szCreateFolders },
7979 { szRemoveIniValues, IDS_DESC_REMOVEINIVALUES, IDS_TEMP_REMOVEINIVALUES, ACTION_RemoveIniValues, szWriteIniValues },
7980 { szRemoveODBC, IDS_DESC_REMOVEODBC, 0, ACTION_RemoveODBC, szInstallODBC },
7981 { szRemoveRegistryValues, IDS_DESC_REMOVEREGISTRYVALUES, IDS_TEMP_REMOVEREGISTRYVALUES, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7982 { szRemoveShortcuts, IDS_DESC_REMOVESHORTCUTS, IDS_TEMP_REMOVESHORTCUTS, ACTION_RemoveShortcuts, szCreateShortcuts },
7983 { szResolveSource, 0, 0, ACTION_ResolveSource, NULL },
7984 { szRMCCPSearch, IDS_DESC_RMCCPSEARCH, 0, ACTION_RMCCPSearch, NULL },
7985 { szScheduleReboot, 0, 0, ACTION_ScheduleReboot, NULL },
7986 { szSelfRegModules, IDS_DESC_SELFREGMODULES, IDS_TEMP_SELFREGMODULES, ACTION_SelfRegModules, szSelfUnregModules },
7987 { szSelfUnregModules, IDS_DESC_SELFUNREGMODULES, IDS_TEMP_SELFUNREGMODULES, ACTION_SelfUnregModules, szSelfRegModules },
7988 { szSetODBCFolders, IDS_DESC_SETODBCFOLDERS, 0, ACTION_SetODBCFolders, NULL },
7989 { szStartServices, IDS_DESC_STARTSERVICES, IDS_TEMP_STARTSERVICES, ACTION_StartServices, szStopServices },
7990 { szStopServices, IDS_DESC_STOPSERVICES, IDS_TEMP_STOPSERVICES, ACTION_StopServices, szStartServices },
7991 { szUnpublishComponents, IDS_DESC_UNPUBLISHCOMPONENTS, IDS_TEMP_UNPUBLISHCOMPONENTS, ACTION_UnpublishComponents, szPublishComponents },
7992 { szUnpublishFeatures, IDS_DESC_UNPUBLISHFEATURES, IDS_TEMP_UNPUBLISHFEATURES, ACTION_UnpublishFeatures, szPublishFeatures },
7993 { szUnpublishProduct, IDS_DESC_UNPUBLISHPRODUCT, 0, ACTION_UnpublishProduct, NULL }, /* for rollback only */
7994 { szUnregisterClassInfo, IDS_DESC_UNREGISTERCLASSINFO, IDS_TEMP_UNREGISTERCLASSINFO, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7995 { szUnregisterComPlus, IDS_DESC_UNREGISTERCOMPLUS, IDS_TEMP_UNREGISTERCOMPLUS, ACTION_UnregisterComPlus, szRegisterComPlus },
7996 { szUnregisterExtensionInfo, IDS_DESC_UNREGISTEREXTENSIONINFO, IDS_TEMP_UNREGISTEREXTENSIONINFO, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7997 { szUnregisterFonts, IDS_DESC_UNREGISTERFONTS, IDS_TEMP_UNREGISTERFONTS, ACTION_UnregisterFonts, szRegisterFonts },
7998 { szUnregisterMIMEInfo, IDS_DESC_UNREGISTERMIMEINFO, IDS_TEMP_UNREGISTERMIMEINFO, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7999 { szUnregisterProgIdInfo, IDS_DESC_UNREGISTERPROGIDINFO, IDS_TEMP_UNREGISTERPROGIDINFO, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
8000 { szUnregisterTypeLibraries, IDS_DESC_UNREGISTERTYPELIBRARIES, IDS_TEMP_UNREGISTERTYPELIBRARIES, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
8001 { szValidateProductID, 0, 0, ACTION_ValidateProductID, NULL },
8002 { szWriteEnvironmentStrings, IDS_DESC_WRITEENVIRONMENTSTRINGS, IDS_TEMP_WRITEENVIRONMENTSTRINGS, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
8003 { szWriteIniValues, IDS_DESC_WRITEINIVALUES, IDS_TEMP_WRITEINIVALUES, ACTION_WriteIniValues, szRemoveIniValues },
8004 { szWriteRegistryValues, IDS_DESC_WRITEREGISTRYVALUES, IDS_TEMP_WRITEREGISTRYVALUES, ACTION_WriteRegistryValues, szRemoveRegistryValues },
8005 { szINSTALL, 0, 0, ACTION_INSTALL, NULL },
8006 { 0 }
8009 static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
8011 UINT rc = ERROR_FUNCTION_NOT_CALLED;
8012 void *cookie;
8013 UINT i;
8015 if (is_wow64 && package->platform == PLATFORM_X64)
8016 Wow64DisableWow64FsRedirection(&cookie);
8018 i = 0;
8019 while (StandardActions[i].action != NULL)
8021 if (!strcmpW( StandardActions[i].action, action ))
8023 WCHAR description[100] = {0}, template[100] = {0};
8025 if (StandardActions[i].description != 0)
8026 LoadStringW(msi_hInstance, StandardActions[i].description, (LPWSTR)&description, 100);
8027 if (StandardActions[i].template != 0)
8028 LoadStringW(msi_hInstance, StandardActions[i].template, (LPWSTR)&template, 100);
8030 ui_actionstart(package, action, description, template);
8031 if (StandardActions[i].handler)
8033 ui_actioninfo( package, action, TRUE, 0 );
8034 rc = StandardActions[i].handler( package );
8035 ui_actioninfo( package, action, FALSE, !rc );
8037 if (StandardActions[i].action_rollback && !package->need_rollback)
8039 TRACE("scheduling rollback action\n");
8040 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
8043 else
8045 FIXME("unhandled standard action %s\n", debugstr_w(action));
8046 rc = ERROR_SUCCESS;
8048 break;
8050 i++;
8053 if (is_wow64 && package->platform == PLATFORM_X64)
8054 Wow64RevertWow64FsRedirection(cookie);
8056 return rc;
8059 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
8061 UINT rc;
8063 TRACE("Performing action (%s)\n", debugstr_w(action));
8065 package->action_progress_increment = 0;
8066 rc = ACTION_HandleStandardAction(package, action);
8068 if (rc == ERROR_FUNCTION_NOT_CALLED)
8069 rc = ACTION_HandleCustomAction(package, action);
8071 if (rc == ERROR_FUNCTION_NOT_CALLED)
8072 WARN("unhandled msi action %s\n", debugstr_w(action));
8074 return rc;
8077 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
8079 UINT rc = ERROR_SUCCESS;
8080 MSIRECORD *row;
8082 static const WCHAR query[] =
8083 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8084 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
8085 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
8086 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
8087 static const WCHAR ui_query[] =
8088 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
8089 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
8090 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
8091 ' ', '=',' ','%','i',0};
8093 if (needs_ui_sequence(package))
8094 row = MSI_QueryGetRecord(package->db, ui_query, seq);
8095 else
8096 row = MSI_QueryGetRecord(package->db, query, seq);
8098 if (row)
8100 LPCWSTR action, cond;
8102 TRACE("Running the actions\n");
8104 /* check conditions */
8105 cond = MSI_RecordGetString(row, 2);
8107 /* this is a hack to skip errors in the condition code */
8108 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
8110 msiobj_release(&row->hdr);
8111 return ERROR_SUCCESS;
8114 action = MSI_RecordGetString(row, 1);
8115 if (!action)
8117 ERR("failed to fetch action\n");
8118 msiobj_release(&row->hdr);
8119 return ERROR_FUNCTION_FAILED;
8122 rc = ACTION_PerformAction(package, action);
8124 msiobj_release(&row->hdr);
8127 return rc;
8130 /****************************************************
8131 * TOP level entry points
8132 *****************************************************/
8134 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
8135 LPCWSTR szCommandLine )
8137 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
8138 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
8139 WCHAR *reinstall = NULL, *productcode, *action;
8140 UINT rc;
8141 DWORD len = 0;
8143 if (szPackagePath)
8145 LPWSTR p, dir;
8146 LPCWSTR file;
8148 dir = strdupW(szPackagePath);
8149 p = strrchrW(dir, '\\');
8150 if (p)
8152 *(++p) = 0;
8153 file = szPackagePath + (p - dir);
8155 else
8157 msi_free(dir);
8158 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
8159 GetCurrentDirectoryW(MAX_PATH, dir);
8160 lstrcatW(dir, szBackSlash);
8161 file = szPackagePath;
8164 msi_free( package->PackagePath );
8165 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
8166 if (!package->PackagePath)
8168 msi_free(dir);
8169 return ERROR_OUTOFMEMORY;
8172 lstrcpyW(package->PackagePath, dir);
8173 lstrcatW(package->PackagePath, file);
8174 msi_free(dir);
8176 msi_set_sourcedir_props(package, FALSE);
8179 rc = msi_parse_command_line( package, szCommandLine, FALSE );
8180 if (rc != ERROR_SUCCESS)
8181 return rc;
8183 msi_apply_transforms( package );
8184 msi_apply_patches( package );
8186 if (msi_get_property( package->db, szAction, NULL, &len ))
8187 msi_set_property( package->db, szAction, szINSTALL, -1 );
8188 action = msi_dup_property( package->db, szAction );
8189 CharUpperW(action);
8191 msi_set_original_database_property( package->db, szPackagePath );
8192 msi_parse_command_line( package, szCommandLine, FALSE );
8193 msi_adjust_privilege_properties( package );
8194 msi_set_context( package );
8196 productcode = msi_dup_property( package->db, szProductCode );
8197 if (strcmpiW( productcode, package->ProductCode ))
8199 TRACE( "product code changed %s -> %s\n", debugstr_w(package->ProductCode), debugstr_w(productcode) );
8200 msi_free( package->ProductCode );
8201 package->ProductCode = productcode;
8203 else msi_free( productcode );
8205 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
8207 TRACE("disabling rollback\n");
8208 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
8211 rc = ACTION_PerformAction(package, action);
8213 /* process the ending type action */
8214 if (rc == ERROR_SUCCESS)
8215 ACTION_PerformActionSequence(package, -1);
8216 else if (rc == ERROR_INSTALL_USEREXIT)
8217 ACTION_PerformActionSequence(package, -2);
8218 else if (rc == ERROR_INSTALL_SUSPEND)
8219 ACTION_PerformActionSequence(package, -4);
8220 else /* failed */
8222 ACTION_PerformActionSequence(package, -3);
8223 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
8225 package->need_rollback = TRUE;
8229 /* finish up running custom actions */
8230 ACTION_FinishCustomActions(package);
8232 if (package->need_rollback && !(reinstall = msi_dup_property( package->db, szReinstall )))
8234 WARN("installation failed, running rollback script\n");
8235 execute_script( package, SCRIPT_ROLLBACK );
8237 msi_free( reinstall );
8238 msi_free( action );
8240 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
8241 return ERROR_SUCCESS_REBOOT_REQUIRED;
8243 return rc;