xmllite: Do not leave start uninitialized (Coverity).
[wine/multimedia.git] / dlls / msi / action.c
blobf519f4b7773b5f2c1bbf0a16fe964c4725343a74
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "shlwapi.h"
39 #include "imagehlp.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
48 static const WCHAR szCreateFolders[] =
49 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
50 static const WCHAR szCostFinalize[] =
51 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
52 static const WCHAR szWriteRegistryValues[] =
53 {'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
54 static const WCHAR szFileCost[] =
55 {'F','i','l','e','C','o','s','t',0};
56 static const WCHAR szInstallInitialize[] =
57 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
58 static const WCHAR szInstallValidate[] =
59 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
60 static const WCHAR szLaunchConditions[] =
61 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
62 static const WCHAR szProcessComponents[] =
63 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
64 static const WCHAR szRegisterTypeLibraries[] =
65 {'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
66 static const WCHAR szCreateShortcuts[] =
67 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
68 static const WCHAR szPublishProduct[] =
69 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
70 static const WCHAR szWriteIniValues[] =
71 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
72 static const WCHAR szSelfRegModules[] =
73 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
74 static const WCHAR szPublishFeatures[] =
75 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
76 static const WCHAR szRegisterProduct[] =
77 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
78 static const WCHAR szInstallExecute[] =
79 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
80 static const WCHAR szInstallExecuteAgain[] =
81 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e','A','g','a','i','n',0};
82 static const WCHAR szInstallFinalize[] =
83 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
84 static const WCHAR szForceReboot[] =
85 {'F','o','r','c','e','R','e','b','o','o','t',0};
86 static const WCHAR szResolveSource[] =
87 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
88 static const WCHAR szAllocateRegistrySpace[] =
89 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y','S','p','a','c','e',0};
90 static const WCHAR szBindImage[] =
91 {'B','i','n','d','I','m','a','g','e',0};
92 static const WCHAR szDeleteServices[] =
93 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
94 static const WCHAR szDisableRollback[] =
95 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
96 static const WCHAR szExecuteAction[] =
97 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
98 static const WCHAR szInstallAdminPackage[] =
99 {'I','n','s','t','a','l','l','A','d','m','i','n','P','a','c','k','a','g','e',0};
100 static const WCHAR szInstallSFPCatalogFile[] =
101 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g','F','i','l','e',0};
102 static const WCHAR szIsolateComponents[] =
103 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
104 static const WCHAR szMigrateFeatureStates[] =
105 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e','S','t','a','t','e','s',0};
106 static const WCHAR szMsiUnpublishAssemblies[] =
107 {'M','s','i','U','n','p','u','b','l','i','s','h','A','s','s','e','m','b','l','i','e','s',0};
108 static const WCHAR szInstallODBC[] =
109 {'I','n','s','t','a','l','l','O','D','B','C',0};
110 static const WCHAR szInstallServices[] =
111 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
112 static const WCHAR szPublishComponents[] =
113 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
114 static const WCHAR szRegisterComPlus[] =
115 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
116 static const WCHAR szRegisterUser[] =
117 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
118 static const WCHAR szRemoveEnvironmentStrings[] =
119 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
120 static const WCHAR szRemoveExistingProducts[] =
121 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g','P','r','o','d','u','c','t','s',0};
122 static const WCHAR szRemoveFolders[] =
123 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
124 static const WCHAR szRemoveIniValues[] =
125 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
126 static const WCHAR szRemoveODBC[] =
127 {'R','e','m','o','v','e','O','D','B','C',0};
128 static const WCHAR szRemoveRegistryValues[] =
129 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
130 static const WCHAR szRemoveShortcuts[] =
131 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
132 static const WCHAR szRMCCPSearch[] =
133 {'R','M','C','C','P','S','e','a','r','c','h',0};
134 static const WCHAR szScheduleReboot[] =
135 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
136 static const WCHAR szSelfUnregModules[] =
137 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
138 static const WCHAR szSetODBCFolders[] =
139 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
140 static const WCHAR szStartServices[] =
141 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
142 static const WCHAR szStopServices[] =
143 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
144 static const WCHAR szUnpublishComponents[] =
145 {'U','n','p','u','b','l','i','s','h', 'C','o','m','p','o','n','e','n','t','s',0};
146 static const WCHAR szUnpublishFeatures[] =
147 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
148 static const WCHAR szUnregisterComPlus[] =
149 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
150 static const WCHAR szUnregisterTypeLibraries[] =
151 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r','i','e','s',0};
152 static const WCHAR szValidateProductID[] =
153 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
154 static const WCHAR szWriteEnvironmentStrings[] =
155 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
157 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
159 static const WCHAR Query_t[] =
160 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
161 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
162 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
163 ' ','\'','%','s','\'',0};
164 MSIRECORD * row;
166 row = MSI_QueryGetRecord( package->db, Query_t, action );
167 if (!row)
168 return;
169 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
170 msiobj_release(&row->hdr);
173 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
174 UINT rc)
176 MSIRECORD * row;
177 static const WCHAR template_s[]=
178 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
179 '%','s', '.',0};
180 static const WCHAR template_e[]=
181 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
182 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
183 '%','i','.',0};
184 static const WCHAR format[] =
185 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
186 WCHAR message[1024];
187 WCHAR timet[0x100];
189 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
190 if (start)
191 sprintfW(message,template_s,timet,action);
192 else
193 sprintfW(message,template_e,timet,action,rc);
195 row = MSI_CreateRecord(1);
196 MSI_RecordSetStringW(row,1,message);
198 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
199 msiobj_release(&row->hdr);
202 enum parse_state
204 state_whitespace,
205 state_token,
206 state_quote
209 static int parse_prop( const WCHAR *str, WCHAR *value, int *quotes )
211 enum parse_state state = state_quote;
212 const WCHAR *p;
213 WCHAR *out = value;
214 int ignore, in_quotes = 0, count = 0, len = 0;
216 for (p = str; *p; p++)
218 ignore = 0;
219 switch (state)
221 case state_whitespace:
222 switch (*p)
224 case ' ':
225 in_quotes = 1;
226 ignore = 1;
227 len++;
228 break;
229 case '"':
230 state = state_quote;
231 if (in_quotes && p[1] != '\"') count--;
232 else count++;
233 break;
234 default:
235 state = state_token;
236 in_quotes = 1;
237 len++;
238 break;
240 break;
242 case state_token:
243 switch (*p)
245 case '"':
246 state = state_quote;
247 if (in_quotes) count--;
248 else count++;
249 break;
250 case ' ':
251 state = state_whitespace;
252 if (!count) goto done;
253 in_quotes = 1;
254 len++;
255 break;
256 default:
257 if (!count) in_quotes = 0;
258 else in_quotes = 1;
259 len++;
260 break;
262 break;
264 case state_quote:
265 switch (*p)
267 case '"':
268 if (in_quotes && p[1] != '\"') count--;
269 else count++;
270 break;
271 case ' ':
272 state = state_whitespace;
273 if (!count || (count > 1 && !len)) goto done;
274 in_quotes = 1;
275 len++;
276 break;
277 default:
278 state = state_token;
279 if (!count) in_quotes = 0;
280 else in_quotes = 1;
281 len++;
282 break;
284 break;
286 default: break;
288 if (!ignore) *out++ = *p;
291 done:
292 if (!len) *value = 0;
293 else *out = 0;
295 *quotes = count;
296 return p - str;
299 static void remove_quotes( WCHAR *str )
301 WCHAR *p = str;
302 int len = strlenW( str );
304 while ((p = strchrW( p, '"' )))
306 memmove( p, p + 1, (len - (p - str)) * sizeof(WCHAR) );
307 p++;
311 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
312 BOOL preserve_case )
314 LPCWSTR ptr, ptr2;
315 int num_quotes;
316 DWORD len;
317 WCHAR *prop, *val;
318 UINT r;
320 if (!szCommandLine)
321 return ERROR_SUCCESS;
323 ptr = szCommandLine;
324 while (*ptr)
326 while (*ptr == ' ') ptr++;
327 if (!*ptr) break;
329 ptr2 = strchrW( ptr, '=' );
330 if (!ptr2) return ERROR_INVALID_COMMAND_LINE;
332 len = ptr2 - ptr;
333 if (!len) return ERROR_INVALID_COMMAND_LINE;
335 prop = msi_alloc( (len + 1) * sizeof(WCHAR) );
336 memcpy( prop, ptr, len * sizeof(WCHAR) );
337 prop[len] = 0;
338 if (!preserve_case) struprW( prop );
340 ptr2++;
341 while (*ptr2 == ' ') ptr2++;
343 num_quotes = 0;
344 val = msi_alloc( (strlenW( ptr2 ) + 1) * sizeof(WCHAR) );
345 len = parse_prop( ptr2, val, &num_quotes );
346 if (num_quotes % 2)
348 WARN("unbalanced quotes\n");
349 msi_free( val );
350 msi_free( prop );
351 return ERROR_INVALID_COMMAND_LINE;
353 remove_quotes( val );
354 TRACE("Found commandline property %s = %s\n", debugstr_w(prop), debugstr_w(val));
356 r = msi_set_property( package->db, prop, val, -1 );
357 if (r == ERROR_SUCCESS && !strcmpW( prop, szSourceDir ))
358 msi_reset_folders( package, TRUE );
360 msi_free( val );
361 msi_free( prop );
363 ptr = ptr2 + len;
366 return ERROR_SUCCESS;
369 WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
371 LPCWSTR pc;
372 LPWSTR p, *ret = NULL;
373 UINT count = 0;
375 if (!str)
376 return ret;
378 /* count the number of substrings */
379 for ( pc = str, count = 0; pc; count++ )
381 pc = strchrW( pc, sep );
382 if (pc)
383 pc++;
386 /* allocate space for an array of substring pointers and the substrings */
387 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
388 (lstrlenW(str)+1) * sizeof(WCHAR) );
389 if (!ret)
390 return ret;
392 /* copy the string and set the pointers */
393 p = (LPWSTR) &ret[count+1];
394 lstrcpyW( p, str );
395 for( count = 0; (ret[count] = p); count++ )
397 p = strchrW( p, sep );
398 if (p)
399 *p++ = 0;
402 return ret;
405 static BOOL ui_sequence_exists( MSIPACKAGE *package )
407 static const WCHAR query [] = {
408 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
409 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
410 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
411 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
412 MSIQUERY *view;
413 UINT rc;
415 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
416 if (rc == ERROR_SUCCESS)
418 msiobj_release(&view->hdr);
419 return TRUE;
421 return FALSE;
424 UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
426 LPWSTR source, check;
428 if (msi_get_property_int( package->db, szInstalled, 0 ))
430 HKEY hkey;
432 MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE );
433 source = msi_reg_get_val_str( hkey, INSTALLPROPERTY_INSTALLSOURCEW );
434 RegCloseKey( hkey );
436 else
438 LPWSTR p, db;
439 DWORD len;
441 db = msi_dup_property( package->db, szOriginalDatabase );
442 if (!db)
443 return ERROR_OUTOFMEMORY;
445 p = strrchrW( db, '\\' );
446 if (!p)
448 p = strrchrW( db, '/' );
449 if (!p)
451 msi_free(db);
452 return ERROR_SUCCESS;
456 len = p - db + 2;
457 source = msi_alloc( len * sizeof(WCHAR) );
458 lstrcpynW( source, db, len );
459 msi_free( db );
462 check = msi_dup_property( package->db, szSourceDir );
463 if (!check || replace)
465 UINT r = msi_set_property( package->db, szSourceDir, source, -1 );
466 if (r == ERROR_SUCCESS)
467 msi_reset_folders( package, TRUE );
469 msi_free( check );
471 check = msi_dup_property( package->db, szSOURCEDIR );
472 if (!check || replace)
473 msi_set_property( package->db, szSOURCEDIR, source, -1 );
475 msi_free( check );
476 msi_free( source );
478 return ERROR_SUCCESS;
481 static BOOL needs_ui_sequence(MSIPACKAGE *package)
483 return (package->ui_level & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED;
486 UINT msi_set_context(MSIPACKAGE *package)
488 UINT r = msi_locate_product( package->ProductCode, &package->Context );
489 if (r != ERROR_SUCCESS)
491 int num = msi_get_property_int( package->db, szAllUsers, 0 );
492 if (num == 1 || num == 2)
493 package->Context = MSIINSTALLCONTEXT_MACHINE;
494 else
495 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
497 return ERROR_SUCCESS;
500 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
502 UINT rc;
503 LPCWSTR cond, action;
504 MSIPACKAGE *package = param;
506 action = MSI_RecordGetString(row,1);
507 if (!action)
509 ERR("Error is retrieving action name\n");
510 return ERROR_FUNCTION_FAILED;
513 /* check conditions */
514 cond = MSI_RecordGetString(row,2);
516 /* this is a hack to skip errors in the condition code */
517 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
519 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
520 return ERROR_SUCCESS;
523 if (needs_ui_sequence(package))
524 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
525 else
526 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
528 msi_dialog_check_messages( NULL );
530 if (package->CurrentInstallState != ERROR_SUCCESS)
531 rc = package->CurrentInstallState;
533 if (rc == ERROR_FUNCTION_NOT_CALLED)
534 rc = ERROR_SUCCESS;
536 if (rc != ERROR_SUCCESS)
537 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
539 if (package->need_reboot_now)
541 TRACE("action %s asked for immediate reboot, suspending installation\n",
542 debugstr_w(action));
543 rc = ACTION_ForceReboot( package );
545 return rc;
548 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR table )
550 static const WCHAR query[] = {
551 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',
552 ' ','W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ',
553 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
554 '`','S','e','q','u','e','n','c','e','`',0};
555 MSIQUERY *view;
556 UINT r;
558 TRACE("%p %s\n", package, debugstr_w(table));
560 r = MSI_OpenQuery( package->db, &view, query, table );
561 if (r == ERROR_SUCCESS)
563 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, package );
564 msiobj_release(&view->hdr);
566 return r;
569 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
571 static const WCHAR query[] = {
572 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
573 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
574 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
575 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
576 'O','R','D','E','R',' ', 'B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
577 static const WCHAR query_validate[] = {
578 'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
579 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
580 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
581 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
582 ' ','\'', 'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','\'',0};
583 MSIQUERY *view;
584 INT seq = 0;
585 UINT rc;
587 if (package->script->ExecuteSequenceRun)
589 TRACE("Execute Sequence already Run\n");
590 return ERROR_SUCCESS;
593 package->script->ExecuteSequenceRun = TRUE;
595 /* get the sequence number */
596 if (UIran)
598 MSIRECORD *row = MSI_QueryGetRecord(package->db, query_validate);
599 if (!row) return ERROR_FUNCTION_FAILED;
600 seq = MSI_RecordGetInteger(row,1);
601 msiobj_release(&row->hdr);
603 rc = MSI_OpenQuery(package->db, &view, query, seq);
604 if (rc == ERROR_SUCCESS)
606 TRACE("Running the actions\n");
608 msi_set_property( package->db, szSourceDir, NULL, -1 );
609 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
610 msiobj_release(&view->hdr);
612 return rc;
615 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
617 static const WCHAR query[] = {
618 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
619 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e','`',' ',
620 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>',' ','0',' ',
621 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0};
622 MSIQUERY *view;
623 UINT rc;
625 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
626 if (rc == ERROR_SUCCESS)
628 TRACE("Running the actions\n");
629 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, package);
630 msiobj_release(&view->hdr);
632 return rc;
635 /********************************************************
636 * ACTION helper functions and functions that perform the actions
637 *******************************************************/
638 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
639 UINT* rc, UINT script, BOOL force )
641 BOOL ret=FALSE;
642 UINT arc;
644 arc = ACTION_CustomAction(package, action, script, force);
646 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
648 *rc = arc;
649 ret = TRUE;
651 return ret;
654 MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
656 MSICOMPONENT *comp;
658 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
660 if (!strcmpW( Component, comp->Component )) return comp;
662 return NULL;
665 MSIFEATURE *msi_get_loaded_feature(MSIPACKAGE* package, const WCHAR *Feature )
667 MSIFEATURE *feature;
669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
671 if (!strcmpW( Feature, feature->Feature )) return feature;
673 return NULL;
676 MSIFILE *msi_get_loaded_file( MSIPACKAGE *package, const WCHAR *key )
678 MSIFILE *file;
680 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
682 if (!strcmpW( key, file->File )) return file;
684 return NULL;
687 MSIFOLDER *msi_get_loaded_folder( MSIPACKAGE *package, const WCHAR *dir )
689 MSIFOLDER *folder;
691 LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
693 if (!strcmpW( dir, folder->Directory )) return folder;
695 return NULL;
699 * Recursively create all directories in the path.
700 * shamelessly stolen from setupapi/queue.c
702 BOOL msi_create_full_path( const WCHAR *path )
704 BOOL ret = TRUE;
705 WCHAR *new_path;
706 int len;
708 new_path = msi_alloc( (strlenW( path ) + 1) * sizeof(WCHAR) );
709 strcpyW( new_path, path );
711 while ((len = strlenW( new_path )) && new_path[len - 1] == '\\')
712 new_path[len - 1] = 0;
714 while (!CreateDirectoryW( new_path, NULL ))
716 WCHAR *slash;
717 DWORD last_error = GetLastError();
718 if (last_error == ERROR_ALREADY_EXISTS) break;
719 if (last_error != ERROR_PATH_NOT_FOUND)
721 ret = FALSE;
722 break;
724 if (!(slash = strrchrW( new_path, '\\' )))
726 ret = FALSE;
727 break;
729 len = slash - new_path;
730 new_path[len] = 0;
731 if (!msi_create_full_path( new_path ))
733 ret = FALSE;
734 break;
736 new_path[len] = '\\';
738 msi_free( new_path );
739 return ret;
742 void msi_ui_progress( MSIPACKAGE *package, int a, int b, int c, int d )
744 MSIRECORD *row;
746 row = MSI_CreateRecord( 4 );
747 MSI_RecordSetInteger( row, 1, a );
748 MSI_RecordSetInteger( row, 2, b );
749 MSI_RecordSetInteger( row, 3, c );
750 MSI_RecordSetInteger( row, 4, d );
751 MSI_ProcessMessage( package, INSTALLMESSAGE_PROGRESS, row );
752 msiobj_release( &row->hdr );
754 msi_dialog_check_messages( NULL );
757 void msi_ui_actiondata( MSIPACKAGE *package, const WCHAR *action, MSIRECORD *record )
759 static const WCHAR query[] =
760 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
761 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
762 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=',' ','\'','%','s','\'',0};
763 WCHAR message[1024];
764 MSIRECORD *row = 0;
765 DWORD size;
767 if (!package->LastAction || strcmpW( package->LastAction, action ))
769 if (!(row = MSI_QueryGetRecord( package->db, query, action ))) return;
771 if (MSI_RecordIsNull( row, 3 ))
773 msiobj_release( &row->hdr );
774 return;
776 /* update the cached action format */
777 msi_free( package->ActionFormat );
778 package->ActionFormat = msi_dup_record_field( row, 3 );
779 msi_free( package->LastAction );
780 package->LastAction = strdupW( action );
781 msiobj_release( &row->hdr );
783 size = 1024;
784 MSI_RecordSetStringW( record, 0, package->ActionFormat );
785 MSI_FormatRecordW( package, record, message, &size );
786 row = MSI_CreateRecord( 1 );
787 MSI_RecordSetStringW( row, 1, message );
788 MSI_ProcessMessage( package, INSTALLMESSAGE_ACTIONDATA, row );
789 msiobj_release( &row->hdr );
792 INSTALLSTATE msi_get_component_action( MSIPACKAGE *package, MSICOMPONENT *comp )
794 if (!comp->Enabled)
796 TRACE("component is disabled: %s\n", debugstr_w(comp->Component));
797 return INSTALLSTATE_UNKNOWN;
799 if (package->need_rollback) return comp->Installed;
800 if (comp->num_clients > 0 && comp->ActionRequest == INSTALLSTATE_ABSENT)
802 TRACE("%s has %u clients left\n", debugstr_w(comp->Component), comp->num_clients);
803 return INSTALLSTATE_UNKNOWN;
805 return comp->ActionRequest;
808 INSTALLSTATE msi_get_feature_action( MSIPACKAGE *package, MSIFEATURE *feature )
810 if (package->need_rollback) return feature->Installed;
811 return feature->ActionRequest;
814 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
816 MSIPACKAGE *package = param;
817 LPCWSTR dir, component, full_path;
818 MSIRECORD *uirow;
819 MSIFOLDER *folder;
820 MSICOMPONENT *comp;
822 component = MSI_RecordGetString(row, 2);
823 if (!component)
824 return ERROR_SUCCESS;
826 comp = msi_get_loaded_component(package, component);
827 if (!comp)
828 return ERROR_SUCCESS;
830 comp->Action = msi_get_component_action( package, comp );
831 if (comp->Action != INSTALLSTATE_LOCAL)
833 TRACE("component not scheduled for installation: %s\n", debugstr_w(component));
834 return ERROR_SUCCESS;
837 dir = MSI_RecordGetString(row,1);
838 if (!dir)
840 ERR("Unable to get folder id\n");
841 return ERROR_SUCCESS;
844 uirow = MSI_CreateRecord(1);
845 MSI_RecordSetStringW(uirow, 1, dir);
846 msi_ui_actiondata(package, szCreateFolders, uirow);
847 msiobj_release(&uirow->hdr);
849 full_path = msi_get_target_folder( package, dir );
850 if (!full_path)
852 ERR("Unable to retrieve folder %s\n", debugstr_w(dir));
853 return ERROR_SUCCESS;
855 TRACE("folder is %s\n", debugstr_w(full_path));
857 folder = msi_get_loaded_folder( package, dir );
858 if (folder->State == FOLDER_STATE_UNINITIALIZED) msi_create_full_path( full_path );
859 folder->State = FOLDER_STATE_CREATED;
860 return ERROR_SUCCESS;
863 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
865 static const WCHAR query[] = {
866 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
867 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
868 MSIQUERY *view;
869 UINT rc;
871 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
872 if (rc != ERROR_SUCCESS)
873 return ERROR_SUCCESS;
875 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
876 msiobj_release(&view->hdr);
877 return rc;
880 static void remove_persistent_folder( MSIFOLDER *folder )
882 FolderList *fl;
884 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
886 remove_persistent_folder( fl->folder );
888 if (folder->persistent && folder->State != FOLDER_STATE_REMOVED)
890 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
894 static UINT ITERATE_RemoveFolders( MSIRECORD *row, LPVOID param )
896 MSIPACKAGE *package = param;
897 LPCWSTR dir, component, full_path;
898 MSIRECORD *uirow;
899 MSIFOLDER *folder;
900 MSICOMPONENT *comp;
902 component = MSI_RecordGetString(row, 2);
903 if (!component)
904 return ERROR_SUCCESS;
906 comp = msi_get_loaded_component(package, component);
907 if (!comp)
908 return ERROR_SUCCESS;
910 comp->Action = msi_get_component_action( package, comp );
911 if (comp->Action != INSTALLSTATE_ABSENT)
913 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
914 return ERROR_SUCCESS;
917 dir = MSI_RecordGetString( row, 1 );
918 if (!dir)
920 ERR("Unable to get folder id\n");
921 return ERROR_SUCCESS;
924 full_path = msi_get_target_folder( package, dir );
925 if (!full_path)
927 ERR("Unable to resolve folder %s\n", debugstr_w(dir));
928 return ERROR_SUCCESS;
930 TRACE("folder is %s\n", debugstr_w(full_path));
932 uirow = MSI_CreateRecord( 1 );
933 MSI_RecordSetStringW( uirow, 1, dir );
934 msi_ui_actiondata( package, szRemoveFolders, uirow );
935 msiobj_release( &uirow->hdr );
937 folder = msi_get_loaded_folder( package, dir );
938 remove_persistent_folder( folder );
939 return ERROR_SUCCESS;
942 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
944 static const WCHAR query[] = {
945 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
946 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0};
947 MSIQUERY *view;
948 UINT rc;
950 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
951 if (rc != ERROR_SUCCESS)
952 return ERROR_SUCCESS;
954 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveFolders, package );
955 msiobj_release( &view->hdr );
956 return rc;
959 static UINT load_component( MSIRECORD *row, LPVOID param )
961 MSIPACKAGE *package = param;
962 MSICOMPONENT *comp;
964 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
965 if (!comp)
966 return ERROR_FUNCTION_FAILED;
968 list_add_tail( &package->components, &comp->entry );
970 /* fill in the data */
971 comp->Component = msi_dup_record_field( row, 1 );
973 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
975 comp->ComponentId = msi_dup_record_field( row, 2 );
976 comp->Directory = msi_dup_record_field( row, 3 );
977 comp->Attributes = MSI_RecordGetInteger(row,4);
978 comp->Condition = msi_dup_record_field( row, 5 );
979 comp->KeyPath = msi_dup_record_field( row, 6 );
981 comp->Installed = INSTALLSTATE_UNKNOWN;
982 comp->Action = INSTALLSTATE_UNKNOWN;
983 comp->ActionRequest = INSTALLSTATE_UNKNOWN;
985 comp->assembly = msi_load_assembly( package, comp );
986 return ERROR_SUCCESS;
989 UINT msi_load_all_components( MSIPACKAGE *package )
991 static const WCHAR query[] = {
992 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
993 '`','C','o','m','p','o','n','e','n','t','`',0};
994 MSIQUERY *view;
995 UINT r;
997 if (!list_empty(&package->components))
998 return ERROR_SUCCESS;
1000 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1001 if (r != ERROR_SUCCESS)
1002 return r;
1004 if (!msi_init_assembly_caches( package ))
1006 ERR("can't initialize assembly caches\n");
1007 msiobj_release( &view->hdr );
1008 return ERROR_FUNCTION_FAILED;
1011 r = MSI_IterateRecords(view, NULL, load_component, package);
1012 msiobj_release(&view->hdr);
1013 return r;
1016 typedef struct {
1017 MSIPACKAGE *package;
1018 MSIFEATURE *feature;
1019 } _ilfs;
1021 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1023 ComponentList *cl;
1025 cl = msi_alloc( sizeof (*cl) );
1026 if ( !cl )
1027 return ERROR_NOT_ENOUGH_MEMORY;
1028 cl->component = comp;
1029 list_add_tail( &feature->Components, &cl->entry );
1031 return ERROR_SUCCESS;
1034 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1036 FeatureList *fl;
1038 fl = msi_alloc( sizeof(*fl) );
1039 if ( !fl )
1040 return ERROR_NOT_ENOUGH_MEMORY;
1041 fl->feature = child;
1042 list_add_tail( &parent->Children, &fl->entry );
1044 return ERROR_SUCCESS;
1047 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1049 _ilfs* ilfs = param;
1050 LPCWSTR component;
1051 MSICOMPONENT *comp;
1053 component = MSI_RecordGetString(row,1);
1055 /* check to see if the component is already loaded */
1056 comp = msi_get_loaded_component( ilfs->package, component );
1057 if (!comp)
1059 WARN("ignoring unknown component %s\n", debugstr_w(component));
1060 return ERROR_SUCCESS;
1062 add_feature_component( ilfs->feature, comp );
1063 comp->Enabled = TRUE;
1065 return ERROR_SUCCESS;
1068 static UINT load_feature(MSIRECORD * row, LPVOID param)
1070 static const WCHAR query[] = {
1071 'S','E','L','E','C','T',' ','`','C','o','m','p','o','n','e','n','t','_','`',
1072 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1073 'C','o','m','p','o','n','e','n','t','s','`',' ','W','H','E','R','E',' ',
1074 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1075 MSIPACKAGE *package = param;
1076 MSIFEATURE *feature;
1077 MSIQUERY *view;
1078 _ilfs ilfs;
1079 UINT rc;
1081 /* fill in the data */
1083 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1084 if (!feature)
1085 return ERROR_NOT_ENOUGH_MEMORY;
1087 list_init( &feature->Children );
1088 list_init( &feature->Components );
1090 feature->Feature = msi_dup_record_field( row, 1 );
1092 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1094 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1095 feature->Title = msi_dup_record_field( row, 3 );
1096 feature->Description = msi_dup_record_field( row, 4 );
1098 if (!MSI_RecordIsNull(row,5))
1099 feature->Display = MSI_RecordGetInteger(row,5);
1101 feature->Level= MSI_RecordGetInteger(row,6);
1102 feature->Directory = msi_dup_record_field( row, 7 );
1103 feature->Attributes = MSI_RecordGetInteger(row,8);
1105 feature->Installed = INSTALLSTATE_UNKNOWN;
1106 feature->Action = INSTALLSTATE_UNKNOWN;
1107 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1109 list_add_tail( &package->features, &feature->entry );
1111 /* load feature components */
1113 rc = MSI_OpenQuery( package->db, &view, query, feature->Feature );
1114 if (rc != ERROR_SUCCESS)
1115 return ERROR_SUCCESS;
1117 ilfs.package = package;
1118 ilfs.feature = feature;
1120 rc = MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1121 msiobj_release(&view->hdr);
1122 return rc;
1125 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1127 MSIPACKAGE *package = param;
1128 MSIFEATURE *parent, *child;
1130 child = msi_get_loaded_feature( package, MSI_RecordGetString( row, 1 ) );
1131 if (!child)
1132 return ERROR_FUNCTION_FAILED;
1134 if (!child->Feature_Parent)
1135 return ERROR_SUCCESS;
1137 parent = msi_get_loaded_feature( package, child->Feature_Parent );
1138 if (!parent)
1139 return ERROR_FUNCTION_FAILED;
1141 add_feature_child( parent, child );
1142 return ERROR_SUCCESS;
1145 UINT msi_load_all_features( MSIPACKAGE *package )
1147 static const WCHAR query[] = {
1148 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1149 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',' ','B','Y',' ',
1150 '`','D','i','s','p','l','a','y','`',0};
1151 MSIQUERY *view;
1152 UINT r;
1154 if (!list_empty(&package->features))
1155 return ERROR_SUCCESS;
1157 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1158 if (r != ERROR_SUCCESS)
1159 return r;
1161 r = MSI_IterateRecords( view, NULL, load_feature, package );
1162 if (r != ERROR_SUCCESS)
1164 msiobj_release( &view->hdr );
1165 return r;
1167 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1168 msiobj_release( &view->hdr );
1169 return r;
1172 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1174 if (!p)
1175 return p;
1176 p = strchrW(p, ch);
1177 if (!p)
1178 return p;
1179 *p = 0;
1180 return p+1;
1183 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1185 static const WCHAR query[] = {
1186 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1187 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1188 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1189 MSIQUERY *view = NULL;
1190 MSIRECORD *row = NULL;
1191 UINT r;
1193 TRACE("%s\n", debugstr_w(file->File));
1195 r = MSI_OpenQuery(package->db, &view, query, file->File);
1196 if (r != ERROR_SUCCESS)
1197 goto done;
1199 r = MSI_ViewExecute(view, NULL);
1200 if (r != ERROR_SUCCESS)
1201 goto done;
1203 r = MSI_ViewFetch(view, &row);
1204 if (r != ERROR_SUCCESS)
1205 goto done;
1207 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1208 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1209 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1210 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1211 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1213 done:
1214 if (view) msiobj_release(&view->hdr);
1215 if (row) msiobj_release(&row->hdr);
1216 return r;
1219 static UINT load_file_disk_id( MSIPACKAGE *package, MSIFILE *file )
1221 MSIRECORD *row;
1222 static const WCHAR query[] = {
1223 'S','E','L','E','C','T',' ','`','D','i','s','k','I','d','`',' ', 'F','R','O','M',' ',
1224 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
1225 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',' ','%','i',0};
1227 row = MSI_QueryGetRecord( package->db, query, file->Sequence );
1228 if (!row)
1230 WARN("query failed\n");
1231 return ERROR_FUNCTION_FAILED;
1234 file->disk_id = MSI_RecordGetInteger( row, 1 );
1235 msiobj_release( &row->hdr );
1236 return ERROR_SUCCESS;
1239 static UINT load_file(MSIRECORD *row, LPVOID param)
1241 MSIPACKAGE* package = param;
1242 LPCWSTR component;
1243 MSIFILE *file;
1245 /* fill in the data */
1247 file = msi_alloc_zero( sizeof (MSIFILE) );
1248 if (!file)
1249 return ERROR_NOT_ENOUGH_MEMORY;
1251 file->File = msi_dup_record_field( row, 1 );
1253 component = MSI_RecordGetString( row, 2 );
1254 file->Component = msi_get_loaded_component( package, component );
1256 if (!file->Component)
1258 WARN("Component not found: %s\n", debugstr_w(component));
1259 msi_free(file->File);
1260 msi_free(file);
1261 return ERROR_SUCCESS;
1264 file->FileName = msi_dup_record_field( row, 3 );
1265 msi_reduce_to_long_filename( file->FileName );
1267 file->ShortName = msi_dup_record_field( row, 3 );
1268 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1270 file->FileSize = MSI_RecordGetInteger( row, 4 );
1271 file->Version = msi_dup_record_field( row, 5 );
1272 file->Language = msi_dup_record_field( row, 6 );
1273 file->Attributes = MSI_RecordGetInteger( row, 7 );
1274 file->Sequence = MSI_RecordGetInteger( row, 8 );
1276 file->state = msifs_invalid;
1278 /* if the compressed bits are not set in the file attributes,
1279 * then read the information from the package word count property
1281 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1283 file->IsCompressed = FALSE;
1285 else if (file->Attributes &
1286 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1288 file->IsCompressed = TRUE;
1290 else if (file->Attributes & msidbFileAttributesNoncompressed)
1292 file->IsCompressed = FALSE;
1294 else
1296 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1299 load_file_hash(package, file);
1300 load_file_disk_id(package, file);
1302 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1304 list_add_tail( &package->files, &file->entry );
1306 return ERROR_SUCCESS;
1309 static UINT load_all_files(MSIPACKAGE *package)
1311 static const WCHAR query[] = {
1312 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1313 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1314 '`','S','e','q','u','e','n','c','e','`', 0};
1315 MSIQUERY *view;
1316 UINT rc;
1318 if (!list_empty(&package->files))
1319 return ERROR_SUCCESS;
1321 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1322 if (rc != ERROR_SUCCESS)
1323 return ERROR_SUCCESS;
1325 rc = MSI_IterateRecords(view, NULL, load_file, package);
1326 msiobj_release(&view->hdr);
1327 return rc;
1330 static UINT load_media( MSIRECORD *row, LPVOID param )
1332 MSIPACKAGE *package = param;
1333 UINT disk_id = MSI_RecordGetInteger( row, 1 );
1334 const WCHAR *cabinet = MSI_RecordGetString( row, 4 );
1336 /* FIXME: load external cabinets and directory sources too */
1337 if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS;
1338 msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet );
1339 return ERROR_SUCCESS;
1342 static UINT load_all_media( MSIPACKAGE *package )
1344 static const WCHAR query[] = {
1345 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`',
1346 'M','e','d','i','a','`',' ','O','R','D','E','R',' ','B','Y',' ',
1347 '`','D','i','s','k','I','d','`',0};
1348 MSIQUERY *view;
1349 UINT r;
1351 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1352 if (r != ERROR_SUCCESS)
1353 return ERROR_SUCCESS;
1355 r = MSI_IterateRecords( view, NULL, load_media, package );
1356 msiobj_release( &view->hdr );
1357 return r;
1360 static UINT load_patch(MSIRECORD *row, LPVOID param)
1362 MSIPACKAGE *package = param;
1363 MSIFILEPATCH *patch;
1364 LPWSTR file_key;
1366 patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
1367 if (!patch)
1368 return ERROR_NOT_ENOUGH_MEMORY;
1370 file_key = msi_dup_record_field( row, 1 );
1371 patch->File = msi_get_loaded_file( package, file_key );
1372 msi_free(file_key);
1374 if( !patch->File )
1376 ERR("Failed to find target for patch in File table\n");
1377 msi_free(patch);
1378 return ERROR_FUNCTION_FAILED;
1381 patch->Sequence = MSI_RecordGetInteger( row, 2 );
1383 /* FIXME: The database should be properly transformed */
1384 patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
1386 patch->PatchSize = MSI_RecordGetInteger( row, 3 );
1387 patch->Attributes = MSI_RecordGetInteger( row, 4 );
1388 patch->IsApplied = FALSE;
1390 /* FIXME:
1391 * Header field - for patch validation.
1392 * _StreamRef - External key into MsiPatchHeaders (instead of the header field)
1395 TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
1397 list_add_tail( &package->filepatches, &patch->entry );
1399 return ERROR_SUCCESS;
1402 static UINT load_all_patches(MSIPACKAGE *package)
1404 static const WCHAR query[] = {
1405 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1406 '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
1407 '`','S','e','q','u','e','n','c','e','`',0};
1408 MSIQUERY *view;
1409 UINT rc;
1411 if (!list_empty(&package->filepatches))
1412 return ERROR_SUCCESS;
1414 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
1415 if (rc != ERROR_SUCCESS)
1416 return ERROR_SUCCESS;
1418 rc = MSI_IterateRecords(view, NULL, load_patch, package);
1419 msiobj_release(&view->hdr);
1420 return rc;
1423 static UINT load_folder_persistence( MSIPACKAGE *package, MSIFOLDER *folder )
1425 static const WCHAR query[] = {
1426 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1427 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',' ','W','H','E','R','E',' ',
1428 '`','D','i','r','e','c','t','o','r','y','_','`',' ','=','\'','%','s','\'',0};
1429 MSIQUERY *view;
1431 folder->persistent = FALSE;
1432 if (!MSI_OpenQuery( package->db, &view, query, folder->Directory ))
1434 if (!MSI_ViewExecute( view, NULL ))
1436 MSIRECORD *rec;
1437 if (!MSI_ViewFetch( view, &rec ))
1439 TRACE("directory %s is persistent\n", debugstr_w(folder->Directory));
1440 folder->persistent = TRUE;
1441 msiobj_release( &rec->hdr );
1444 msiobj_release( &view->hdr );
1446 return ERROR_SUCCESS;
1449 static UINT load_folder( MSIRECORD *row, LPVOID param )
1451 MSIPACKAGE *package = param;
1452 static WCHAR szEmpty[] = { 0 };
1453 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1454 MSIFOLDER *folder;
1456 if (!(folder = msi_alloc_zero( sizeof(*folder) ))) return ERROR_NOT_ENOUGH_MEMORY;
1457 list_init( &folder->children );
1458 folder->Directory = msi_dup_record_field( row, 1 );
1459 folder->Parent = msi_dup_record_field( row, 2 );
1460 p = msi_dup_record_field(row, 3);
1462 TRACE("%s\n", debugstr_w(folder->Directory));
1464 /* split src and target dir */
1465 tgt_short = p;
1466 src_short = folder_split_path( p, ':' );
1468 /* split the long and short paths */
1469 tgt_long = folder_split_path( tgt_short, '|' );
1470 src_long = folder_split_path( src_short, '|' );
1472 /* check for no-op dirs */
1473 if (tgt_short && !strcmpW( szDot, tgt_short ))
1474 tgt_short = szEmpty;
1475 if (src_short && !strcmpW( szDot, src_short ))
1476 src_short = szEmpty;
1478 if (!tgt_long)
1479 tgt_long = tgt_short;
1481 if (!src_short) {
1482 src_short = tgt_short;
1483 src_long = tgt_long;
1486 if (!src_long)
1487 src_long = src_short;
1489 /* FIXME: use the target short path too */
1490 folder->TargetDefault = strdupW(tgt_long);
1491 folder->SourceShortPath = strdupW(src_short);
1492 folder->SourceLongPath = strdupW(src_long);
1493 msi_free(p);
1495 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1496 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1497 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1499 load_folder_persistence( package, folder );
1501 list_add_tail( &package->folders, &folder->entry );
1502 return ERROR_SUCCESS;
1505 static UINT add_folder_child( MSIFOLDER *parent, MSIFOLDER *child )
1507 FolderList *fl;
1509 if (!(fl = msi_alloc( sizeof(*fl) ))) return ERROR_NOT_ENOUGH_MEMORY;
1510 fl->folder = child;
1511 list_add_tail( &parent->children, &fl->entry );
1512 return ERROR_SUCCESS;
1515 static UINT find_folder_children( MSIRECORD *row, LPVOID param )
1517 MSIPACKAGE *package = param;
1518 MSIFOLDER *parent, *child;
1520 if (!(child = msi_get_loaded_folder( package, MSI_RecordGetString( row, 1 ) )))
1521 return ERROR_FUNCTION_FAILED;
1523 if (!child->Parent) return ERROR_SUCCESS;
1525 if (!(parent = msi_get_loaded_folder( package, child->Parent )))
1526 return ERROR_FUNCTION_FAILED;
1528 return add_folder_child( parent, child );
1531 static UINT load_all_folders( MSIPACKAGE *package )
1533 static const WCHAR query[] = {
1534 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1535 '`','D','i','r','e','c','t','o','r','y','`',0};
1536 MSIQUERY *view;
1537 UINT r;
1539 if (!list_empty(&package->folders))
1540 return ERROR_SUCCESS;
1542 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1543 if (r != ERROR_SUCCESS)
1544 return r;
1546 r = MSI_IterateRecords( view, NULL, load_folder, package );
1547 if (r != ERROR_SUCCESS)
1549 msiobj_release( &view->hdr );
1550 return r;
1552 r = MSI_IterateRecords( view, NULL, find_folder_children, package );
1553 msiobj_release( &view->hdr );
1554 return r;
1557 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1559 msi_set_property( package->db, szCostingComplete, szZero, -1 );
1560 msi_set_property( package->db, szRootDrive, szCRoot, -1 );
1562 load_all_folders( package );
1563 msi_load_all_components( package );
1564 msi_load_all_features( package );
1565 load_all_files( package );
1566 load_all_patches( package );
1567 load_all_media( package );
1569 return ERROR_SUCCESS;
1572 static UINT execute_script_action( MSIPACKAGE *package, UINT script, UINT index )
1574 const WCHAR *action = package->script->Actions[script][index];
1575 ui_actionstart( package, action );
1576 TRACE("executing %s\n", debugstr_w(action));
1577 return ACTION_PerformAction( package, action, script );
1580 static UINT execute_script( MSIPACKAGE *package, UINT script )
1582 UINT i, rc = ERROR_SUCCESS;
1584 TRACE("executing script %u\n", script);
1586 if (!package->script)
1588 ERR("no script!\n");
1589 return ERROR_FUNCTION_FAILED;
1591 if (script == SCRIPT_ROLLBACK)
1593 for (i = package->script->ActionCount[script]; i > 0; i--)
1595 rc = execute_script_action( package, script, i - 1 );
1596 if (rc != ERROR_SUCCESS) break;
1599 else
1601 for (i = 0; i < package->script->ActionCount[script]; i++)
1603 rc = execute_script_action( package, script, i );
1604 if (rc != ERROR_SUCCESS) break;
1607 msi_free_action_script(package, script);
1608 return rc;
1611 static UINT ACTION_FileCost(MSIPACKAGE *package)
1613 return ERROR_SUCCESS;
1616 static void get_client_counts( MSIPACKAGE *package )
1618 MSICOMPONENT *comp;
1619 HKEY hkey;
1621 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1623 if (!comp->ComponentId) continue;
1625 if (MSIREG_OpenUserDataComponentKey( comp->ComponentId, szLocalSid, &hkey, FALSE ) &&
1626 MSIREG_OpenUserDataComponentKey( comp->ComponentId, NULL, &hkey, FALSE ))
1628 comp->num_clients = 0;
1629 continue;
1631 RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, NULL, NULL, (DWORD *)&comp->num_clients,
1632 NULL, NULL, NULL, NULL );
1633 RegCloseKey( hkey );
1637 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1639 MSICOMPONENT *comp;
1640 UINT r;
1642 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1644 if (!comp->ComponentId) continue;
1646 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1647 MSIINSTALLCONTEXT_USERMANAGED, comp->ComponentId,
1648 &comp->Installed );
1649 if (r == ERROR_SUCCESS) continue;
1651 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1652 MSIINSTALLCONTEXT_USERUNMANAGED, comp->ComponentId,
1653 &comp->Installed );
1654 if (r == ERROR_SUCCESS) continue;
1656 r = MsiQueryComponentStateW( package->ProductCode, NULL,
1657 MSIINSTALLCONTEXT_MACHINE, comp->ComponentId,
1658 &comp->Installed );
1659 if (r == ERROR_SUCCESS) continue;
1661 comp->Installed = INSTALLSTATE_ABSENT;
1665 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1667 MSIFEATURE *feature;
1669 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1671 INSTALLSTATE state = MsiQueryFeatureStateW( package->ProductCode, feature->Feature );
1673 if (state == INSTALLSTATE_UNKNOWN || state == INSTALLSTATE_INVALIDARG)
1674 feature->Installed = INSTALLSTATE_ABSENT;
1675 else
1676 feature->Installed = state;
1680 static inline BOOL is_feature_selected( MSIFEATURE *feature, INT level )
1682 return (feature->Level > 0 && feature->Level <= level);
1685 static BOOL process_state_property(MSIPACKAGE* package, int level,
1686 LPCWSTR property, INSTALLSTATE state)
1688 LPWSTR override;
1689 MSIFEATURE *feature;
1691 override = msi_dup_property( package->db, property );
1692 if (!override)
1693 return FALSE;
1695 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1697 if (strcmpW( property, szRemove ) && !is_feature_selected( feature, level ))
1698 continue;
1700 if (!strcmpW(property, szReinstall)) state = feature->Installed;
1702 if (!strcmpiW( override, szAll ))
1704 if (feature->Installed != state)
1706 feature->Action = state;
1707 feature->ActionRequest = state;
1710 else
1712 LPWSTR ptr = override;
1713 LPWSTR ptr2 = strchrW(override,',');
1715 while (ptr)
1717 int len = ptr2 - ptr;
1719 if ((ptr2 && strlenW(feature->Feature) == len && !strncmpW(ptr, feature->Feature, len))
1720 || (!ptr2 && !strcmpW(ptr, feature->Feature)))
1722 if (feature->Installed != state)
1724 feature->Action = state;
1725 feature->ActionRequest = state;
1727 break;
1729 if (ptr2)
1731 ptr=ptr2+1;
1732 ptr2 = strchrW(ptr,',');
1734 else
1735 break;
1739 msi_free(override);
1740 return TRUE;
1743 static BOOL process_overrides( MSIPACKAGE *package, int level )
1745 static const WCHAR szAddLocal[] =
1746 {'A','D','D','L','O','C','A','L',0};
1747 static const WCHAR szAddSource[] =
1748 {'A','D','D','S','O','U','R','C','E',0};
1749 static const WCHAR szAdvertise[] =
1750 {'A','D','V','E','R','T','I','S','E',0};
1751 BOOL ret = FALSE;
1753 /* all these activation/deactivation things happen in order and things
1754 * later on the list override things earlier on the list.
1756 * 0 INSTALLLEVEL processing
1757 * 1 ADDLOCAL
1758 * 2 REMOVE
1759 * 3 ADDSOURCE
1760 * 4 ADDDEFAULT
1761 * 5 REINSTALL
1762 * 6 ADVERTISE
1763 * 7 COMPADDLOCAL
1764 * 8 COMPADDSOURCE
1765 * 9 FILEADDLOCAL
1766 * 10 FILEADDSOURCE
1767 * 11 FILEADDDEFAULT
1769 ret |= process_state_property( package, level, szAddLocal, INSTALLSTATE_LOCAL );
1770 ret |= process_state_property( package, level, szRemove, INSTALLSTATE_ABSENT );
1771 ret |= process_state_property( package, level, szAddSource, INSTALLSTATE_SOURCE );
1772 ret |= process_state_property( package, level, szReinstall, INSTALLSTATE_UNKNOWN );
1773 ret |= process_state_property( package, level, szAdvertise, INSTALLSTATE_ADVERTISED );
1775 if (ret && !package->full_reinstall)
1776 msi_set_property( package->db, szPreselected, szOne, -1 );
1778 return ret;
1781 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1783 int level;
1784 MSICOMPONENT* component;
1785 MSIFEATURE *feature;
1787 TRACE("Checking Install Level\n");
1789 level = msi_get_property_int(package->db, szInstallLevel, 1);
1791 if (!msi_get_property_int( package->db, szPreselected, 0 ))
1793 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1795 if (!is_feature_selected( feature, level )) continue;
1797 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1799 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1801 feature->Action = INSTALLSTATE_SOURCE;
1802 feature->ActionRequest = INSTALLSTATE_SOURCE;
1804 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1806 feature->Action = INSTALLSTATE_ADVERTISED;
1807 feature->ActionRequest = INSTALLSTATE_ADVERTISED;
1809 else
1811 feature->Action = INSTALLSTATE_LOCAL;
1812 feature->ActionRequest = INSTALLSTATE_LOCAL;
1816 /* disable child features of unselected parent or follow parent */
1817 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1819 FeatureList *fl;
1821 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1823 if (!is_feature_selected( feature, level ))
1825 fl->feature->Action = INSTALLSTATE_UNKNOWN;
1826 fl->feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1828 else if (fl->feature->Attributes & msidbFeatureAttributesFollowParent)
1830 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1831 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1832 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1833 fl->feature->Action = feature->Action;
1834 fl->feature->ActionRequest = feature->ActionRequest;
1839 else /* preselected */
1841 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1843 if (!is_feature_selected( feature, level )) continue;
1845 if (feature->ActionRequest == INSTALLSTATE_UNKNOWN)
1847 if (feature->Installed == INSTALLSTATE_ABSENT)
1849 feature->Action = INSTALLSTATE_UNKNOWN;
1850 feature->ActionRequest = INSTALLSTATE_UNKNOWN;
1852 else
1854 feature->Action = feature->Installed;
1855 feature->ActionRequest = feature->Installed;
1859 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1861 FeatureList *fl;
1863 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1865 if (fl->feature->Attributes & msidbFeatureAttributesFollowParent &&
1866 (!(feature->Attributes & msidbFeatureAttributesFavorAdvertise)))
1868 TRACE("feature %s (level %d request %d) follows parent %s (level %d request %d)\n",
1869 debugstr_w(fl->feature->Feature), fl->feature->Level, fl->feature->ActionRequest,
1870 debugstr_w(feature->Feature), feature->Level, feature->ActionRequest);
1871 fl->feature->Action = feature->Action;
1872 fl->feature->ActionRequest = feature->ActionRequest;
1878 /* now we want to set component state based based on feature state */
1879 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1881 ComponentList *cl;
1883 TRACE("examining feature %s (level %d installed %d request %d action %d)\n",
1884 debugstr_w(feature->Feature), feature->Level, feature->Installed,
1885 feature->ActionRequest, feature->Action);
1887 if (!is_feature_selected( feature, level )) continue;
1889 /* features with components that have compressed files are made local */
1890 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1892 if (cl->component->ForceLocalState &&
1893 feature->ActionRequest == INSTALLSTATE_SOURCE)
1895 feature->Action = INSTALLSTATE_LOCAL;
1896 feature->ActionRequest = INSTALLSTATE_LOCAL;
1897 break;
1901 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1903 component = cl->component;
1905 switch (feature->ActionRequest)
1907 case INSTALLSTATE_ABSENT:
1908 component->anyAbsent = 1;
1909 break;
1910 case INSTALLSTATE_ADVERTISED:
1911 component->hasAdvertiseFeature = 1;
1912 break;
1913 case INSTALLSTATE_SOURCE:
1914 component->hasSourceFeature = 1;
1915 break;
1916 case INSTALLSTATE_LOCAL:
1917 component->hasLocalFeature = 1;
1918 break;
1919 case INSTALLSTATE_DEFAULT:
1920 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1921 component->hasAdvertiseFeature = 1;
1922 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1923 component->hasSourceFeature = 1;
1924 else
1925 component->hasLocalFeature = 1;
1926 break;
1927 default:
1928 break;
1933 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1935 /* check if it's local or source */
1936 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1937 (component->hasLocalFeature || component->hasSourceFeature))
1939 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1940 !component->ForceLocalState)
1942 component->Action = INSTALLSTATE_SOURCE;
1943 component->ActionRequest = INSTALLSTATE_SOURCE;
1945 else
1947 component->Action = INSTALLSTATE_LOCAL;
1948 component->ActionRequest = INSTALLSTATE_LOCAL;
1950 continue;
1953 /* if any feature is local, the component must be local too */
1954 if (component->hasLocalFeature)
1956 component->Action = INSTALLSTATE_LOCAL;
1957 component->ActionRequest = INSTALLSTATE_LOCAL;
1958 continue;
1960 if (component->hasSourceFeature)
1962 component->Action = INSTALLSTATE_SOURCE;
1963 component->ActionRequest = INSTALLSTATE_SOURCE;
1964 continue;
1966 if (component->hasAdvertiseFeature)
1968 component->Action = INSTALLSTATE_ADVERTISED;
1969 component->ActionRequest = INSTALLSTATE_ADVERTISED;
1970 continue;
1972 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1973 if (component->anyAbsent && component->ComponentId)
1975 component->Action = INSTALLSTATE_ABSENT;
1976 component->ActionRequest = INSTALLSTATE_ABSENT;
1980 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1982 if (component->ActionRequest == INSTALLSTATE_DEFAULT)
1984 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1985 component->Action = INSTALLSTATE_LOCAL;
1986 component->ActionRequest = INSTALLSTATE_LOCAL;
1989 if (component->ActionRequest == INSTALLSTATE_SOURCE &&
1990 component->Installed == INSTALLSTATE_SOURCE &&
1991 component->hasSourceFeature)
1993 component->Action = INSTALLSTATE_UNKNOWN;
1994 component->ActionRequest = INSTALLSTATE_UNKNOWN;
1997 TRACE("component %s (installed %d request %d action %d)\n",
1998 debugstr_w(component->Component), component->Installed, component->ActionRequest, component->Action);
2000 if (component->Action == INSTALLSTATE_LOCAL || component->Action == INSTALLSTATE_SOURCE)
2001 component->num_clients++;
2002 else if (component->Action == INSTALLSTATE_ABSENT)
2003 component->num_clients--;
2006 return ERROR_SUCCESS;
2009 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2011 MSIPACKAGE *package = param;
2012 LPCWSTR name;
2013 MSIFEATURE *feature;
2015 name = MSI_RecordGetString( row, 1 );
2017 feature = msi_get_loaded_feature( package, name );
2018 if (!feature)
2019 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2020 else
2022 LPCWSTR Condition;
2023 Condition = MSI_RecordGetString(row,3);
2025 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2027 int level = MSI_RecordGetInteger(row,2);
2028 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2029 feature->Level = level;
2032 return ERROR_SUCCESS;
2035 VS_FIXEDFILEINFO *msi_get_disk_file_version( LPCWSTR filename )
2037 static const WCHAR name[] = {'\\',0};
2038 VS_FIXEDFILEINFO *ptr, *ret;
2039 LPVOID version;
2040 DWORD versize, handle;
2041 UINT sz;
2043 versize = GetFileVersionInfoSizeW( filename, &handle );
2044 if (!versize)
2045 return NULL;
2047 version = msi_alloc( versize );
2048 if (!version)
2049 return NULL;
2051 GetFileVersionInfoW( filename, 0, versize, version );
2053 if (!VerQueryValueW( version, name, (LPVOID *)&ptr, &sz ))
2055 msi_free( version );
2056 return NULL;
2059 ret = msi_alloc( sz );
2060 memcpy( ret, ptr, sz );
2062 msi_free( version );
2063 return ret;
2066 int msi_compare_file_versions( VS_FIXEDFILEINFO *fi, const WCHAR *version )
2068 DWORD ms, ls;
2070 msi_parse_version_string( version, &ms, &ls );
2072 if (fi->dwFileVersionMS > ms) return 1;
2073 else if (fi->dwFileVersionMS < ms) return -1;
2074 else if (fi->dwFileVersionLS > ls) return 1;
2075 else if (fi->dwFileVersionLS < ls) return -1;
2076 return 0;
2079 int msi_compare_font_versions( const WCHAR *ver1, const WCHAR *ver2 )
2081 DWORD ms1, ms2;
2083 msi_parse_version_string( ver1, &ms1, NULL );
2084 msi_parse_version_string( ver2, &ms2, NULL );
2086 if (ms1 > ms2) return 1;
2087 else if (ms1 < ms2) return -1;
2088 return 0;
2091 DWORD msi_get_disk_file_size( LPCWSTR filename )
2093 HANDLE file;
2094 DWORD size;
2096 file = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
2097 if (file == INVALID_HANDLE_VALUE)
2098 return INVALID_FILE_SIZE;
2100 size = GetFileSize( file, NULL );
2101 TRACE("size is %u\n", size);
2102 CloseHandle( file );
2103 return size;
2106 BOOL msi_file_hash_matches( MSIFILE *file )
2108 UINT r;
2109 MSIFILEHASHINFO hash;
2111 hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
2112 r = MsiGetFileHashW( file->TargetPath, 0, &hash );
2113 if (r != ERROR_SUCCESS)
2114 return FALSE;
2116 return !memcmp( &hash, &file->hash, sizeof(MSIFILEHASHINFO) );
2119 static WCHAR *get_temp_dir( void )
2121 static UINT id;
2122 WCHAR tmp[MAX_PATH], dir[MAX_PATH];
2124 GetTempPathW( MAX_PATH, tmp );
2125 for (;;)
2127 if (!GetTempFileNameW( tmp, szMsi, ++id, dir )) return NULL;
2128 if (CreateDirectoryW( dir, NULL )) break;
2130 return strdupW( dir );
2134 * msi_build_directory_name()
2136 * This function is to save messing round with directory names
2137 * It handles adding backslashes between path segments,
2138 * and can add \ at the end of the directory name if told to.
2140 * It takes a variable number of arguments.
2141 * It always allocates a new string for the result, so make sure
2142 * to free the return value when finished with it.
2144 * The first arg is the number of path segments that follow.
2145 * The arguments following count are a list of path segments.
2146 * A path segment may be NULL.
2148 * Path segments will be added with a \ separating them.
2149 * A \ will not be added after the last segment, however if the
2150 * last segment is NULL, then the last character will be a \
2152 WCHAR *msi_build_directory_name( DWORD count, ... )
2154 DWORD sz = 1, i;
2155 WCHAR *dir;
2156 va_list va;
2158 va_start( va, count );
2159 for (i = 0; i < count; i++)
2161 const WCHAR *str = va_arg( va, const WCHAR * );
2162 if (str) sz += strlenW( str ) + 1;
2164 va_end( va );
2166 dir = msi_alloc( sz * sizeof(WCHAR) );
2167 dir[0] = 0;
2169 va_start( va, count );
2170 for (i = 0; i < count; i++)
2172 const WCHAR *str = va_arg( va, const WCHAR * );
2173 if (!str) continue;
2174 strcatW( dir, str );
2175 if ( i + 1 != count && dir[0] && dir[strlenW( dir ) - 1] != '\\') strcatW( dir, szBackSlash );
2177 va_end( va );
2178 return dir;
2181 static void set_target_path( MSIPACKAGE *package, MSIFILE *file )
2183 MSIASSEMBLY *assembly = file->Component->assembly;
2185 TRACE("file %s is named %s\n", debugstr_w(file->File), debugstr_w(file->FileName));
2187 msi_free( file->TargetPath );
2188 if (assembly && !assembly->application)
2190 if (!assembly->tempdir) assembly->tempdir = get_temp_dir();
2191 file->TargetPath = msi_build_directory_name( 2, assembly->tempdir, file->FileName );
2192 msi_track_tempfile( package, file->TargetPath );
2194 else
2196 const WCHAR *dir = msi_get_target_folder( package, file->Component->Directory );
2197 file->TargetPath = msi_build_directory_name( 2, dir, file->FileName );
2200 TRACE("resolves to %s\n", debugstr_w(file->TargetPath));
2203 static UINT calculate_file_cost( MSIPACKAGE *package )
2205 VS_FIXEDFILEINFO *file_version;
2206 WCHAR *font_version;
2207 MSIFILE *file;
2209 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2211 MSICOMPONENT *comp = file->Component;
2212 DWORD file_size;
2214 if (!comp->Enabled) continue;
2216 if (file->IsCompressed)
2217 comp->ForceLocalState = TRUE;
2219 set_target_path( package, file );
2221 if ((comp->assembly && !comp->assembly->installed) ||
2222 GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2224 comp->Cost += file->FileSize;
2225 continue;
2227 file_size = msi_get_disk_file_size( file->TargetPath );
2229 if (file->Version)
2231 if ((file_version = msi_get_disk_file_version( file->TargetPath )))
2233 if (msi_compare_file_versions( file_version, file->Version ) < 0)
2235 comp->Cost += file->FileSize - file_size;
2237 msi_free( file_version );
2238 continue;
2240 else if ((font_version = msi_font_version_from_file( file->TargetPath )))
2242 if (msi_compare_font_versions( font_version, file->Version ) < 0)
2244 comp->Cost += file->FileSize - file_size;
2246 msi_free( font_version );
2247 continue;
2250 if (file_size != file->FileSize)
2252 comp->Cost += file->FileSize - file_size;
2255 return ERROR_SUCCESS;
2258 WCHAR *msi_normalize_path( const WCHAR *in )
2260 const WCHAR *p = in;
2261 WCHAR *q, *ret;
2262 int n, len = strlenW( in ) + 2;
2264 if (!(q = ret = msi_alloc( len * sizeof(WCHAR) ))) return NULL;
2266 len = 0;
2267 while (1)
2269 /* copy until the end of the string or a space */
2270 while (*p != ' ' && (*q = *p))
2272 p++, len++;
2273 /* reduce many backslashes to one */
2274 if (*p != '\\' || *q != '\\')
2275 q++;
2278 /* quit at the end of the string */
2279 if (!*p)
2280 break;
2282 /* count the number of spaces */
2283 n = 0;
2284 while (p[n] == ' ')
2285 n++;
2287 /* if it's leading or trailing space, skip it */
2288 if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
2289 p += n;
2290 else /* copy n spaces */
2291 while (n && (*q++ = *p++)) n--;
2293 while (q - ret > 0 && q[-1] == ' ') q--;
2294 if (q - ret > 0 && q[-1] != '\\')
2296 q[0] = '\\';
2297 q[1] = 0;
2299 return ret;
2302 static WCHAR *get_install_location( MSIPACKAGE *package )
2304 HKEY hkey;
2305 WCHAR *path;
2307 if (!package->ProductCode) return NULL;
2308 if (MSIREG_OpenInstallProps( package->ProductCode, package->Context, NULL, &hkey, FALSE )) return NULL;
2309 if ((path = msi_reg_get_val_str( hkey, szInstallLocation )) && !path[0])
2311 msi_free( path );
2312 path = NULL;
2314 RegCloseKey( hkey );
2315 return path;
2318 void msi_resolve_target_folder( MSIPACKAGE *package, const WCHAR *name, BOOL load_prop )
2320 FolderList *fl;
2321 MSIFOLDER *folder, *parent, *child;
2322 WCHAR *path, *normalized_path;
2324 TRACE("resolving %s\n", debugstr_w(name));
2326 if (!(folder = msi_get_loaded_folder( package, name ))) return;
2328 if (!strcmpW( folder->Directory, szTargetDir )) /* special resolving for target root dir */
2330 if (!(path = get_install_location( package )) &&
2331 (!load_prop || !(path = msi_dup_property( package->db, szTargetDir ))))
2333 path = msi_dup_property( package->db, szRootDrive );
2336 else if (!load_prop || !(path = msi_dup_property( package->db, folder->Directory )))
2338 if (folder->Parent && strcmpW( folder->Directory, folder->Parent ))
2340 parent = msi_get_loaded_folder( package, folder->Parent );
2341 path = msi_build_directory_name( 3, parent->ResolvedTarget, folder->TargetDefault, NULL );
2343 else
2344 path = msi_build_directory_name( 2, folder->TargetDefault, NULL );
2346 normalized_path = msi_normalize_path( path );
2347 msi_free( path );
2348 if (folder->ResolvedTarget && !strcmpiW( normalized_path, folder->ResolvedTarget ))
2350 TRACE("%s already resolved to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2351 msi_free( normalized_path );
2352 return;
2354 msi_set_property( package->db, folder->Directory, normalized_path, -1 );
2355 msi_free( folder->ResolvedTarget );
2356 folder->ResolvedTarget = normalized_path;
2358 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
2360 child = fl->folder;
2361 msi_resolve_target_folder( package, child->Directory, load_prop );
2363 TRACE("%s resolves to %s\n", debugstr_w(name), debugstr_w(folder->ResolvedTarget));
2366 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2368 static const WCHAR query[] =
2369 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2370 '`','C','o','n','d','i','t','i','o','n','`',0};
2371 static const WCHAR szOutOfDiskSpace[] =
2372 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2373 static const WCHAR szPrimaryFolder[] =
2374 {'P','R','I','M','A','R','Y','F','O','L','D','E','R',0};
2375 static const WCHAR szPrimaryVolumePath[] =
2376 {'P','r','i','m','a','r','y','V','o','l','u','m','e','P','a','t','h',0};
2377 static const WCHAR szPrimaryVolumeSpaceAvailable[] =
2378 {'P','r','i','m','a','r','y','V','o','l','u','m','e','S','p','a','c','e',
2379 'A','v','a','i','l','a','b','l','e',0};
2380 MSICOMPONENT *comp;
2381 MSIQUERY *view;
2382 WCHAR *level, *primary_key, *primary_folder;
2383 UINT rc;
2385 TRACE("Building directory properties\n");
2386 msi_resolve_target_folder( package, szTargetDir, TRUE );
2388 TRACE("Evaluating component conditions\n");
2389 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2391 if (MSI_EvaluateConditionW( package, comp->Condition ) == MSICONDITION_FALSE)
2393 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2394 comp->Enabled = FALSE;
2396 else
2397 comp->Enabled = TRUE;
2399 get_client_counts( package );
2401 /* read components states from the registry */
2402 ACTION_GetComponentInstallStates(package);
2403 ACTION_GetFeatureInstallStates(package);
2405 if (!process_overrides( package, msi_get_property_int( package->db, szInstallLevel, 1 ) ))
2407 TRACE("Evaluating feature conditions\n");
2409 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
2410 if (rc == ERROR_SUCCESS)
2412 rc = MSI_IterateRecords( view, NULL, ITERATE_CostFinalizeConditions, package );
2413 msiobj_release( &view->hdr );
2414 if (rc != ERROR_SUCCESS)
2415 return rc;
2419 TRACE("Calculating file cost\n");
2420 calculate_file_cost( package );
2422 msi_set_property( package->db, szCostingComplete, szOne, -1 );
2423 /* set default run level if not set */
2424 level = msi_dup_property( package->db, szInstallLevel );
2425 if (!level) msi_set_property( package->db, szInstallLevel, szOne, -1 );
2426 msi_free(level);
2428 if ((primary_key = msi_dup_property( package->db, szPrimaryFolder )))
2430 if ((primary_folder = msi_dup_property( package->db, primary_key )))
2432 if (((primary_folder[0] >= 'A' && primary_folder[0] <= 'Z') ||
2433 (primary_folder[0] >= 'a' && primary_folder[0] <= 'z')) && primary_folder[1] == ':')
2435 ULARGE_INTEGER free;
2437 primary_folder[2] = 0;
2438 if (GetDiskFreeSpaceExW( primary_folder, &free, NULL, NULL ))
2440 static const WCHAR fmtW[] = {'%','l','u',0};
2441 WCHAR buf[21];
2443 sprintfW( buf, fmtW, free.QuadPart / 512 );
2444 msi_set_property( package->db, szPrimaryVolumeSpaceAvailable, buf, -1 );
2446 toupperW( primary_folder[0] );
2447 msi_set_property( package->db, szPrimaryVolumePath, primary_folder, 2 );
2449 msi_free( primary_folder );
2451 msi_free( primary_key );
2454 /* FIXME: check volume disk space */
2455 msi_set_property( package->db, szOutOfDiskSpace, szZero, -1 );
2457 return MSI_SetFeatureStates(package);
2460 static BYTE *parse_value( MSIPACKAGE *package, const WCHAR *value, DWORD *type, DWORD *size )
2462 BYTE *data = NULL;
2464 if (!value)
2466 *size = sizeof(WCHAR);
2467 *type = REG_SZ;
2468 if ((data = msi_alloc( *size ))) *(WCHAR *)data = 0;
2469 return data;
2471 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2473 if (value[1]=='x')
2475 LPWSTR ptr;
2476 CHAR byte[5];
2477 LPWSTR deformated = NULL;
2478 int count;
2480 deformat_string(package, &value[2], &deformated);
2482 /* binary value type */
2483 ptr = deformated;
2484 *type = REG_BINARY;
2485 if (strlenW(ptr)%2)
2486 *size = (strlenW(ptr)/2)+1;
2487 else
2488 *size = strlenW(ptr)/2;
2490 data = msi_alloc(*size);
2492 byte[0] = '0';
2493 byte[1] = 'x';
2494 byte[4] = 0;
2495 count = 0;
2496 /* if uneven pad with a zero in front */
2497 if (strlenW(ptr)%2)
2499 byte[2]= '0';
2500 byte[3]= *ptr;
2501 ptr++;
2502 data[count] = (BYTE)strtol(byte,NULL,0);
2503 count ++;
2504 TRACE("Uneven byte count\n");
2506 while (*ptr)
2508 byte[2]= *ptr;
2509 ptr++;
2510 byte[3]= *ptr;
2511 ptr++;
2512 data[count] = (BYTE)strtol(byte,NULL,0);
2513 count ++;
2515 msi_free(deformated);
2517 TRACE("Data %i bytes(%i)\n",*size,count);
2519 else
2521 LPWSTR deformated;
2522 LPWSTR p;
2523 DWORD d = 0;
2524 deformat_string(package, &value[1], &deformated);
2526 *type=REG_DWORD;
2527 *size = sizeof(DWORD);
2528 data = msi_alloc(*size);
2529 p = deformated;
2530 if (*p == '-')
2531 p++;
2532 while (*p)
2534 if ( (*p < '0') || (*p > '9') )
2535 break;
2536 d *= 10;
2537 d += (*p - '0');
2538 p++;
2540 if (deformated[0] == '-')
2541 d = -d;
2542 *(LPDWORD)data = d;
2543 TRACE("DWORD %i\n",*(LPDWORD)data);
2545 msi_free(deformated);
2548 else
2550 const WCHAR *ptr = value;
2551 DWORD len;
2553 *type = REG_SZ;
2554 if (value[0] == '#')
2556 ptr++;
2557 if (value[1] == '%')
2559 ptr++;
2560 *type = REG_EXPAND_SZ;
2563 len = deformat_string( package, ptr, (WCHAR **)&data );
2564 if (len > strlenW( (const WCHAR *)data )) *type = REG_MULTI_SZ;
2565 *size = (len + 1) * sizeof(WCHAR);
2567 return data;
2570 static const WCHAR *get_root_key( MSIPACKAGE *package, INT root, HKEY *root_key )
2572 const WCHAR *ret;
2574 switch (root)
2576 case -1:
2577 if (msi_get_property_int( package->db, szAllUsers, 0 ))
2579 *root_key = HKEY_LOCAL_MACHINE;
2580 ret = szHLM;
2582 else
2584 *root_key = HKEY_CURRENT_USER;
2585 ret = szHCU;
2587 break;
2588 case 0:
2589 *root_key = HKEY_CLASSES_ROOT;
2590 ret = szHCR;
2591 break;
2592 case 1:
2593 *root_key = HKEY_CURRENT_USER;
2594 ret = szHCU;
2595 break;
2596 case 2:
2597 *root_key = HKEY_LOCAL_MACHINE;
2598 ret = szHLM;
2599 break;
2600 case 3:
2601 *root_key = HKEY_USERS;
2602 ret = szHU;
2603 break;
2604 default:
2605 ERR("Unknown root %i\n", root);
2606 return NULL;
2609 return ret;
2612 static WCHAR *get_keypath( MSICOMPONENT *comp, HKEY root, const WCHAR *path )
2614 static const WCHAR prefixW[] = {'S','O','F','T','W','A','R','E','\\'};
2615 static const UINT len = sizeof(prefixW) / sizeof(prefixW[0]);
2617 if ((is_64bit || is_wow64) &&
2618 !(comp->Attributes & msidbComponentAttributes64bit) &&
2619 root == HKEY_LOCAL_MACHINE && !strncmpiW( path, prefixW, len ))
2621 UINT size;
2622 WCHAR *path_32node;
2624 size = (strlenW( path ) + strlenW( szWow6432Node ) + 2) * sizeof(WCHAR);
2625 if (!(path_32node = msi_alloc( size ))) return NULL;
2627 memcpy( path_32node, path, len * sizeof(WCHAR) );
2628 strcpyW( path_32node + len, szWow6432Node );
2629 strcatW( path_32node, szBackSlash );
2630 strcatW( path_32node, path + len );
2631 return path_32node;
2633 return strdupW( path );
2636 static HKEY open_key( HKEY root, const WCHAR *path, BOOL create )
2638 REGSAM access = KEY_ALL_ACCESS;
2639 WCHAR *subkey, *p, *q;
2640 HKEY hkey, ret = NULL;
2641 LONG res;
2643 if (is_wow64) access |= KEY_WOW64_64KEY;
2645 if (!(subkey = strdupW( path ))) return NULL;
2646 p = subkey;
2647 if ((q = strchrW( p, '\\' ))) *q = 0;
2648 if (create)
2649 res = RegCreateKeyExW( root, subkey, 0, NULL, 0, access, NULL, &hkey, NULL );
2650 else
2651 res = RegOpenKeyExW( root, subkey, 0, access, &hkey );
2652 if (res)
2654 TRACE("failed to open key %s (%d)\n", debugstr_w(subkey), res);
2655 msi_free( subkey );
2656 return NULL;
2658 if (q && q[1])
2660 ret = open_key( hkey, q + 1, create );
2661 RegCloseKey( hkey );
2663 else ret = hkey;
2664 msi_free( subkey );
2665 return ret;
2668 static BOOL is_special_entry( const WCHAR *name )
2670 return (name && (name[0] == '*' || name[0] == '+') && !name[1]);
2673 static WCHAR **split_multi_string_values( const WCHAR *str, DWORD len, DWORD *count )
2675 const WCHAR *p = str;
2676 WCHAR **ret;
2677 int i = 0;
2679 *count = 0;
2680 if (!str) return NULL;
2681 while ((p - str) < len)
2683 p += strlenW( p ) + 1;
2684 (*count)++;
2686 if (!(ret = msi_alloc( *count * sizeof(WCHAR *) ))) return NULL;
2687 p = str;
2688 while ((p - str) < len)
2690 if (!(ret[i] = strdupW( p )))
2692 for (; i >= 0; i--) msi_free( ret[i] );
2693 msi_free( ret );
2694 return NULL;
2696 p += strlenW( p ) + 1;
2697 i++;
2699 return ret;
2702 static WCHAR *flatten_multi_string_values( WCHAR **left, DWORD left_count,
2703 WCHAR **right, DWORD right_count, DWORD *size )
2705 WCHAR *ret, *p;
2706 unsigned int i;
2708 *size = sizeof(WCHAR);
2709 for (i = 0; i < left_count; i++) *size += (strlenW( left[i] ) + 1) * sizeof(WCHAR);
2710 for (i = 0; i < right_count; i++) *size += (strlenW( right[i] ) + 1) * sizeof(WCHAR);
2712 if (!(ret = p = msi_alloc( *size ))) return NULL;
2714 for (i = 0; i < left_count; i++)
2716 strcpyW( p, left[i] );
2717 p += strlenW( p ) + 1;
2719 for (i = 0; i < right_count; i++)
2721 strcpyW( p, right[i] );
2722 p += strlenW( p ) + 1;
2724 *p = 0;
2725 return ret;
2728 static DWORD remove_duplicate_values( WCHAR **old, DWORD old_count,
2729 WCHAR **new, DWORD new_count )
2731 DWORD ret = old_count;
2732 unsigned int i, j, k;
2734 for (i = 0; i < new_count; i++)
2736 for (j = 0; j < old_count; j++)
2738 if (old[j] && !strcmpW( new[i], old[j] ))
2740 msi_free( old[j] );
2741 for (k = j; k < old_count - 1; k++) { old[k] = old[k + 1]; }
2742 old[k] = NULL;
2743 ret--;
2747 return ret;
2750 enum join_op
2752 JOIN_OP_APPEND,
2753 JOIN_OP_PREPEND,
2754 JOIN_OP_REPLACE
2757 static WCHAR *join_multi_string_values( enum join_op op, WCHAR **old, DWORD old_count,
2758 WCHAR **new, DWORD new_count, DWORD *size )
2760 switch (op)
2762 case JOIN_OP_APPEND:
2763 old_count = remove_duplicate_values( old, old_count, new, new_count );
2764 return flatten_multi_string_values( old, old_count, new, new_count, size );
2766 case JOIN_OP_PREPEND:
2767 old_count = remove_duplicate_values( old, old_count, new, new_count );
2768 return flatten_multi_string_values( new, new_count, old, old_count, size );
2770 case JOIN_OP_REPLACE:
2771 return flatten_multi_string_values( new, new_count, NULL, 0, size );
2773 default:
2774 ERR("unhandled join op %u\n", op);
2775 return NULL;
2779 static BYTE *build_multi_string_value( BYTE *old_value, DWORD old_size,
2780 BYTE *new_value, DWORD new_size, DWORD *size )
2782 DWORD i, old_len = 0, new_len = 0, old_count = 0, new_count = 0;
2783 const WCHAR *new_ptr = NULL, *old_ptr = NULL;
2784 enum join_op op = JOIN_OP_REPLACE;
2785 WCHAR **old = NULL, **new = NULL;
2786 BYTE *ret;
2788 if (new_size / sizeof(WCHAR) - 1 > 1)
2790 new_ptr = (const WCHAR *)new_value;
2791 new_len = new_size / sizeof(WCHAR) - 1;
2793 if (!new_ptr[0] && new_ptr[new_len - 1])
2795 op = JOIN_OP_APPEND;
2796 new_len--;
2797 new_ptr++;
2799 else if (new_ptr[0] && !new_ptr[new_len - 1])
2801 op = JOIN_OP_PREPEND;
2802 new_len--;
2804 else if (new_len > 2 && !new_ptr[0] && !new_ptr[new_len - 1])
2806 op = JOIN_OP_REPLACE;
2807 new_len -= 2;
2808 new_ptr++;
2810 new = split_multi_string_values( new_ptr, new_len, &new_count );
2812 if (old_size / sizeof(WCHAR) - 1 > 1)
2814 old_ptr = (const WCHAR *)old_value;
2815 old_len = old_size / sizeof(WCHAR) - 1;
2816 old = split_multi_string_values( old_ptr, old_len, &old_count );
2818 ret = (BYTE *)join_multi_string_values( op, old, old_count, new, new_count, size );
2819 for (i = 0; i < old_count; i++) msi_free( old[i] );
2820 for (i = 0; i < new_count; i++) msi_free( new[i] );
2821 msi_free( old );
2822 msi_free( new );
2823 return ret;
2826 static BYTE *reg_get_value( HKEY hkey, const WCHAR *name, DWORD *type, DWORD *size )
2828 BYTE *ret;
2829 if (RegQueryValueExW( hkey, name, NULL, NULL, NULL, size )) return NULL;
2830 if (!(ret = msi_alloc( *size ))) return NULL;
2831 RegQueryValueExW( hkey, name, NULL, type, ret, size );
2832 return ret;
2835 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2837 MSIPACKAGE *package = param;
2838 BYTE *new_value, *old_value = NULL;
2839 HKEY root_key, hkey;
2840 DWORD type, old_type, new_size, old_size = 0;
2841 LPWSTR deformated, uikey, keypath;
2842 const WCHAR *szRoot, *component, *name, *key, *str;
2843 MSICOMPONENT *comp;
2844 MSIRECORD * uirow;
2845 INT root;
2846 BOOL check_first = FALSE;
2847 int len;
2849 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
2851 component = MSI_RecordGetString(row, 6);
2852 comp = msi_get_loaded_component(package,component);
2853 if (!comp)
2854 return ERROR_SUCCESS;
2856 comp->Action = msi_get_component_action( package, comp );
2857 if (comp->Action != INSTALLSTATE_LOCAL)
2859 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
2860 return ERROR_SUCCESS;
2863 name = MSI_RecordGetString(row, 4);
2864 if( MSI_RecordIsNull(row,5) && name )
2866 /* null values can have special meanings */
2867 if (name[0]=='-' && name[1] == 0)
2868 return ERROR_SUCCESS;
2869 if ((name[0] == '+' || name[0] == '*') && !name[1])
2870 check_first = TRUE;
2873 root = MSI_RecordGetInteger(row,2);
2874 key = MSI_RecordGetString(row, 3);
2876 szRoot = get_root_key( package, root, &root_key );
2877 if (!szRoot)
2878 return ERROR_SUCCESS;
2880 deformat_string(package, key , &deformated);
2881 uikey = msi_alloc( (strlenW(deformated) + strlenW(szRoot) + 1) * sizeof(WCHAR) );
2882 strcpyW(uikey,szRoot);
2883 strcatW(uikey,deformated);
2885 keypath = get_keypath( comp, root_key, deformated );
2886 msi_free( deformated );
2887 if (!(hkey = open_key( root_key, keypath, TRUE )))
2889 ERR("Could not create key %s\n", debugstr_w(keypath));
2890 msi_free(uikey);
2891 msi_free(keypath);
2892 return ERROR_FUNCTION_FAILED;
2894 str = msi_record_get_string( row, 5, &len );
2895 if (str && len > strlenW( str ))
2897 type = REG_MULTI_SZ;
2898 new_size = (len + 1) * sizeof(WCHAR);
2899 new_value = (BYTE *)msi_strdupW( str, len );
2901 else new_value = parse_value( package, str, &type, &new_size );
2902 deformat_string(package, name, &deformated);
2904 if (!is_special_entry( name ))
2906 old_value = reg_get_value( hkey, deformated, &old_type, &old_size );
2907 if (type == REG_MULTI_SZ)
2909 BYTE *new;
2910 if (old_value && old_type != REG_MULTI_SZ)
2912 msi_free( old_value );
2913 old_value = NULL;
2914 old_size = 0;
2916 new = build_multi_string_value( old_value, old_size, new_value, new_size, &new_size );
2917 msi_free( new_value );
2918 new_value = new;
2920 if (!check_first)
2922 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2923 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2925 else if (!old_value)
2927 if (deformated || new_size)
2929 TRACE("setting value %s of %s type %u\n", debugstr_w(deformated), debugstr_w(uikey), type);
2930 RegSetValueExW( hkey, deformated, 0, type, new_value, new_size );
2933 else TRACE("not overwriting existing value %s of %s\n", debugstr_w(deformated), debugstr_w(uikey));
2935 RegCloseKey(hkey);
2937 uirow = MSI_CreateRecord(3);
2938 MSI_RecordSetStringW(uirow,2,deformated);
2939 MSI_RecordSetStringW(uirow,1,uikey);
2940 if (type == REG_SZ || type == REG_EXPAND_SZ)
2941 MSI_RecordSetStringW(uirow, 3, (LPWSTR)new_value);
2942 msi_ui_actiondata( package, szWriteRegistryValues, uirow );
2943 msiobj_release( &uirow->hdr );
2945 msi_free(new_value);
2946 msi_free(old_value);
2947 msi_free(deformated);
2948 msi_free(uikey);
2949 msi_free(keypath);
2951 return ERROR_SUCCESS;
2954 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2956 static const WCHAR query[] = {
2957 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2958 '`','R','e','g','i','s','t','r','y','`',0};
2959 MSIQUERY *view;
2960 UINT rc;
2962 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
2963 if (rc != ERROR_SUCCESS)
2964 return ERROR_SUCCESS;
2966 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2967 msiobj_release(&view->hdr);
2968 return rc;
2971 static void delete_key( HKEY root, const WCHAR *path )
2973 REGSAM access = 0;
2974 WCHAR *subkey, *p;
2975 HKEY hkey;
2976 LONG res;
2978 if (is_wow64) access |= KEY_WOW64_64KEY;
2980 if (!(subkey = strdupW( path ))) return;
2981 for (;;)
2983 if ((p = strrchrW( subkey, '\\' ))) *p = 0;
2984 hkey = open_key( root, subkey, FALSE );
2985 if (!hkey) break;
2986 if (p && p[1])
2987 res = RegDeleteKeyExW( hkey, p + 1, access, 0 );
2988 else
2989 res = RegDeleteKeyExW( root, subkey, access, 0 );
2990 if (res)
2992 TRACE("failed to delete key %s (%d)\n", debugstr_w(subkey), res);
2993 break;
2995 if (p && p[1]) RegCloseKey( hkey );
2996 else break;
2998 msi_free( subkey );
3001 static void delete_value( HKEY root, const WCHAR *path, const WCHAR *value )
3003 LONG res;
3004 HKEY hkey;
3005 DWORD num_subkeys, num_values;
3007 if ((hkey = open_key( root, path, FALSE )))
3009 if ((res = RegDeleteValueW( hkey, value )))
3010 TRACE("failed to delete value %s (%d)\n", debugstr_w(value), res);
3012 res = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, &num_subkeys, NULL, NULL, &num_values,
3013 NULL, NULL, NULL, NULL );
3014 RegCloseKey( hkey );
3015 if (!res && !num_subkeys && !num_values)
3017 TRACE("removing empty key %s\n", debugstr_w(path));
3018 delete_key( root, path );
3023 static void delete_tree( HKEY root, const WCHAR *path )
3025 LONG res;
3026 HKEY hkey;
3028 if (!(hkey = open_key( root, path, FALSE ))) return;
3029 res = RegDeleteTreeW( hkey, NULL );
3030 if (res) TRACE("failed to delete subtree of %s (%d)\n", debugstr_w(path), res);
3031 delete_key( root, path );
3032 RegCloseKey( hkey );
3035 static UINT ITERATE_RemoveRegistryValuesOnUninstall( MSIRECORD *row, LPVOID param )
3037 MSIPACKAGE *package = param;
3038 LPCWSTR component, name, key_str, root_key_str;
3039 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3040 MSICOMPONENT *comp;
3041 MSIRECORD *uirow;
3042 BOOL delete_key = FALSE;
3043 HKEY hkey_root;
3044 UINT size;
3045 INT root;
3047 msi_ui_progress( package, 2, REG_PROGRESS_VALUE, 0, 0 );
3049 component = MSI_RecordGetString( row, 6 );
3050 comp = msi_get_loaded_component( package, component );
3051 if (!comp)
3052 return ERROR_SUCCESS;
3054 comp->Action = msi_get_component_action( package, comp );
3055 if (comp->Action != INSTALLSTATE_ABSENT)
3057 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3058 return ERROR_SUCCESS;
3061 name = MSI_RecordGetString( row, 4 );
3062 if (MSI_RecordIsNull( row, 5 ) && name )
3064 if (name[0] == '+' && !name[1])
3065 return ERROR_SUCCESS;
3066 if ((name[0] == '-' || name[0] == '*') && !name[1])
3068 delete_key = TRUE;
3069 name = NULL;
3073 root = MSI_RecordGetInteger( row, 2 );
3074 key_str = MSI_RecordGetString( row, 3 );
3076 root_key_str = get_root_key( package, root, &hkey_root );
3077 if (!root_key_str)
3078 return ERROR_SUCCESS;
3080 deformat_string( package, key_str, &deformated_key );
3081 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3082 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3083 strcpyW( ui_key_str, root_key_str );
3084 strcatW( ui_key_str, deformated_key );
3086 deformat_string( package, name, &deformated_name );
3088 keypath = get_keypath( comp, hkey_root, deformated_key );
3089 msi_free( deformated_key );
3090 if (delete_key) delete_tree( hkey_root, keypath );
3091 else delete_value( hkey_root, keypath, deformated_name );
3092 msi_free( keypath );
3094 uirow = MSI_CreateRecord( 2 );
3095 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3096 MSI_RecordSetStringW( uirow, 2, deformated_name );
3097 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3098 msiobj_release( &uirow->hdr );
3100 msi_free( ui_key_str );
3101 msi_free( deformated_name );
3102 return ERROR_SUCCESS;
3105 static UINT ITERATE_RemoveRegistryValuesOnInstall( MSIRECORD *row, LPVOID param )
3107 MSIPACKAGE *package = param;
3108 LPCWSTR component, name, key_str, root_key_str;
3109 LPWSTR deformated_key, deformated_name, ui_key_str, keypath;
3110 MSICOMPONENT *comp;
3111 MSIRECORD *uirow;
3112 BOOL delete_key = FALSE;
3113 HKEY hkey_root;
3114 UINT size;
3115 INT root;
3117 component = MSI_RecordGetString( row, 5 );
3118 comp = msi_get_loaded_component( package, component );
3119 if (!comp)
3120 return ERROR_SUCCESS;
3122 comp->Action = msi_get_component_action( package, comp );
3123 if (comp->Action != INSTALLSTATE_LOCAL)
3125 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3126 return ERROR_SUCCESS;
3129 if ((name = MSI_RecordGetString( row, 4 )))
3131 if (name[0] == '-' && !name[1])
3133 delete_key = TRUE;
3134 name = NULL;
3138 root = MSI_RecordGetInteger( row, 2 );
3139 key_str = MSI_RecordGetString( row, 3 );
3141 root_key_str = get_root_key( package, root, &hkey_root );
3142 if (!root_key_str)
3143 return ERROR_SUCCESS;
3145 deformat_string( package, key_str, &deformated_key );
3146 size = strlenW( deformated_key ) + strlenW( root_key_str ) + 1;
3147 ui_key_str = msi_alloc( size * sizeof(WCHAR) );
3148 strcpyW( ui_key_str, root_key_str );
3149 strcatW( ui_key_str, deformated_key );
3151 deformat_string( package, name, &deformated_name );
3153 keypath = get_keypath( comp, hkey_root, deformated_key );
3154 msi_free( deformated_key );
3155 if (delete_key) delete_tree( hkey_root, keypath );
3156 else delete_value( hkey_root, keypath, deformated_name );
3157 msi_free( keypath );
3159 uirow = MSI_CreateRecord( 2 );
3160 MSI_RecordSetStringW( uirow, 1, ui_key_str );
3161 MSI_RecordSetStringW( uirow, 2, deformated_name );
3162 msi_ui_actiondata( package, szRemoveRegistryValues, uirow );
3163 msiobj_release( &uirow->hdr );
3165 msi_free( ui_key_str );
3166 msi_free( deformated_name );
3167 return ERROR_SUCCESS;
3170 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
3172 static const WCHAR registry_query[] = {
3173 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3174 '`','R','e','g','i','s','t','r','y','`',0};
3175 static const WCHAR remove_registry_query[] = {
3176 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3177 '`','R','e','m','o','v','e','R','e','g','i','s','t','r','y','`',0};
3178 MSIQUERY *view;
3179 UINT rc;
3181 rc = MSI_DatabaseOpenViewW( package->db, registry_query, &view );
3182 if (rc == ERROR_SUCCESS)
3184 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnUninstall, package );
3185 msiobj_release( &view->hdr );
3186 if (rc != ERROR_SUCCESS)
3187 return rc;
3189 rc = MSI_DatabaseOpenViewW( package->db, remove_registry_query, &view );
3190 if (rc == ERROR_SUCCESS)
3192 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveRegistryValuesOnInstall, package );
3193 msiobj_release( &view->hdr );
3194 if (rc != ERROR_SUCCESS)
3195 return rc;
3197 return ERROR_SUCCESS;
3200 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
3202 package->script->CurrentlyScripting = TRUE;
3204 return ERROR_SUCCESS;
3208 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
3210 static const WCHAR query[]= {
3211 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3212 '`','R','e','g','i','s','t','r','y','`',0};
3213 MSICOMPONENT *comp;
3214 DWORD total = 0, count = 0;
3215 MSIQUERY *view;
3216 MSIFEATURE *feature;
3217 MSIFILE *file;
3218 UINT rc;
3220 TRACE("InstallValidate\n");
3222 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3223 if (rc == ERROR_SUCCESS)
3225 rc = MSI_IterateRecords( view, &count, NULL, package );
3226 msiobj_release( &view->hdr );
3227 if (rc != ERROR_SUCCESS)
3228 return rc;
3229 total += count * REG_PROGRESS_VALUE;
3231 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3232 total += COMPONENT_PROGRESS_VALUE;
3234 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3235 total += file->FileSize;
3237 msi_ui_progress( package, 0, total, 0, 0 );
3239 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3241 TRACE("Feature: %s Installed %d Request %d Action %d\n",
3242 debugstr_w(feature->Feature), feature->Installed,
3243 feature->ActionRequest, feature->Action);
3245 return ERROR_SUCCESS;
3248 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
3250 MSIPACKAGE* package = param;
3251 LPCWSTR cond = NULL;
3252 LPCWSTR message = NULL;
3253 UINT r;
3255 static const WCHAR title[]=
3256 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
3258 cond = MSI_RecordGetString(row,1);
3260 r = MSI_EvaluateConditionW(package,cond);
3261 if (r == MSICONDITION_FALSE)
3263 if ((package->ui_level & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
3265 LPWSTR deformated;
3266 message = MSI_RecordGetString(row,2);
3267 deformat_string(package,message,&deformated);
3268 MessageBoxW(NULL,deformated,title,MB_OK);
3269 msi_free(deformated);
3272 return ERROR_INSTALL_FAILURE;
3275 return ERROR_SUCCESS;
3278 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
3280 static const WCHAR query[] = {
3281 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3282 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
3283 MSIQUERY *view;
3284 UINT rc;
3286 TRACE("Checking launch conditions\n");
3288 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3289 if (rc != ERROR_SUCCESS)
3290 return ERROR_SUCCESS;
3292 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
3293 msiobj_release(&view->hdr);
3294 return rc;
3297 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
3300 if (!cmp->KeyPath)
3301 return strdupW( msi_get_target_folder( package, cmp->Directory ) );
3303 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
3305 static const WCHAR query[] = {
3306 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3307 '`','R','e','g','i','s','t','r','y','`',' ','W','H','E','R','E',' ',
3308 '`','R','e','g','i','s','t','r','y','`',' ','=',' ' ,'\'','%','s','\'',0};
3309 static const WCHAR fmt[] = {'%','0','2','i',':','\\','%','s','\\',0};
3310 static const WCHAR fmt2[]= {'%','0','2','i',':','\\','%','s','\\','%','s',0};
3311 MSIRECORD *row;
3312 UINT root, len;
3313 LPWSTR deformated, buffer, deformated_name;
3314 LPCWSTR key, name;
3316 row = MSI_QueryGetRecord(package->db, query, cmp->KeyPath);
3317 if (!row)
3318 return NULL;
3320 root = MSI_RecordGetInteger(row,2);
3321 key = MSI_RecordGetString(row, 3);
3322 name = MSI_RecordGetString(row, 4);
3323 deformat_string(package, key , &deformated);
3324 deformat_string(package, name, &deformated_name);
3326 len = strlenW(deformated) + 6;
3327 if (deformated_name)
3328 len+=strlenW(deformated_name);
3330 buffer = msi_alloc( len *sizeof(WCHAR));
3332 if (deformated_name)
3333 sprintfW(buffer,fmt2,root,deformated,deformated_name);
3334 else
3335 sprintfW(buffer,fmt,root,deformated);
3337 msi_free(deformated);
3338 msi_free(deformated_name);
3339 msiobj_release(&row->hdr);
3341 return buffer;
3343 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
3345 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
3346 return NULL;
3348 else
3350 MSIFILE *file = msi_get_loaded_file( package, cmp->KeyPath );
3352 if (file)
3353 return strdupW( file->TargetPath );
3355 return NULL;
3358 static HKEY openSharedDLLsKey(void)
3360 HKEY hkey=0;
3361 static const WCHAR path[] =
3362 {'S','o','f','t','w','a','r','e','\\',
3363 'M','i','c','r','o','s','o','f','t','\\',
3364 'W','i','n','d','o','w','s','\\',
3365 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
3366 'S','h','a','r','e','d','D','L','L','s',0};
3368 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
3369 return hkey;
3372 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
3374 HKEY hkey;
3375 DWORD count=0;
3376 DWORD type;
3377 DWORD sz = sizeof(count);
3378 DWORD rc;
3380 hkey = openSharedDLLsKey();
3381 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
3382 if (rc != ERROR_SUCCESS)
3383 count = 0;
3384 RegCloseKey(hkey);
3385 return count;
3388 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
3390 HKEY hkey;
3392 hkey = openSharedDLLsKey();
3393 if (count > 0)
3394 msi_reg_set_val_dword( hkey, path, count );
3395 else
3396 RegDeleteValueW(hkey,path);
3397 RegCloseKey(hkey);
3398 return count;
3401 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
3403 MSIFEATURE *feature;
3404 INT count = 0;
3405 BOOL write = FALSE;
3407 /* only refcount DLLs */
3408 if (comp->KeyPath == NULL ||
3409 comp->assembly ||
3410 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
3411 comp->Attributes & msidbComponentAttributesODBCDataSource)
3412 write = FALSE;
3413 else
3415 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
3416 write = (count > 0);
3418 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
3419 write = TRUE;
3422 /* increment counts */
3423 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3425 ComponentList *cl;
3427 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_LOCAL)
3428 continue;
3430 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3432 if ( cl->component == comp )
3433 count++;
3437 /* decrement counts */
3438 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3440 ComponentList *cl;
3442 if (msi_get_feature_action( package, feature ) != INSTALLSTATE_ABSENT)
3443 continue;
3445 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3447 if ( cl->component == comp )
3448 count--;
3452 /* ref count all the files in the component */
3453 if (write)
3455 MSIFILE *file;
3457 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
3459 if (file->Component == comp)
3460 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
3464 /* add a count for permanent */
3465 if (comp->Attributes & msidbComponentAttributesPermanent)
3466 count ++;
3468 comp->RefCount = count;
3470 if (write)
3471 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
3474 static WCHAR *build_full_keypath( MSIPACKAGE *package, MSICOMPONENT *comp )
3476 if (comp->assembly)
3478 const WCHAR prefixW[] = {'<','\\',0};
3479 DWORD len = strlenW( prefixW ) + strlenW( comp->assembly->display_name );
3480 WCHAR *keypath = msi_alloc( (len + 1) * sizeof(WCHAR) );
3482 if (keypath)
3484 strcpyW( keypath, prefixW );
3485 strcatW( keypath, comp->assembly->display_name );
3487 return keypath;
3489 return resolve_keypath( package, comp );
3492 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
3494 WCHAR squished_pc[GUID_SIZE], squished_cc[GUID_SIZE];
3495 UINT rc;
3496 MSICOMPONENT *comp;
3497 HKEY hkey;
3499 TRACE("\n");
3501 squash_guid(package->ProductCode,squished_pc);
3502 msi_set_sourcedir_props(package, FALSE);
3504 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
3506 MSIRECORD *uirow;
3507 INSTALLSTATE action;
3509 msi_ui_progress( package, 2, COMPONENT_PROGRESS_VALUE, 0, 0 );
3510 if (!comp->ComponentId)
3511 continue;
3513 squash_guid( comp->ComponentId, squished_cc );
3514 msi_free( comp->FullKeypath );
3515 comp->FullKeypath = build_full_keypath( package, comp );
3517 ACTION_RefCountComponent( package, comp );
3519 if (package->need_rollback) action = comp->Installed;
3520 else action = comp->ActionRequest;
3522 TRACE("Component %s (%s) Keypath=%s RefCount=%u Clients=%u Action=%u\n",
3523 debugstr_w(comp->Component), debugstr_w(squished_cc),
3524 debugstr_w(comp->FullKeypath), comp->RefCount, comp->num_clients, action);
3526 if (action == INSTALLSTATE_LOCAL || action == INSTALLSTATE_SOURCE)
3528 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3529 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid, &hkey, TRUE);
3530 else
3531 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL, &hkey, TRUE);
3533 if (rc != ERROR_SUCCESS)
3534 continue;
3536 if (comp->Attributes & msidbComponentAttributesPermanent)
3538 static const WCHAR szPermKey[] =
3539 { '0','0','0','0','0','0','0','0','0','0','0','0',
3540 '0','0','0','0','0','0','0','0','0','0','0','0',
3541 '0','0','0','0','0','0','0','0',0 };
3543 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
3545 if (action == INSTALLSTATE_LOCAL)
3546 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
3547 else
3549 MSIFILE *file;
3550 MSIRECORD *row;
3551 LPWSTR ptr, ptr2;
3552 WCHAR source[MAX_PATH];
3553 WCHAR base[MAX_PATH];
3554 LPWSTR sourcepath;
3556 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3557 static const WCHAR query[] = {
3558 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3559 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3560 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3561 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3562 '`','D','i','s','k','I','d','`',0};
3564 if (!comp->KeyPath || !(file = msi_get_loaded_file(package, comp->KeyPath)))
3565 continue;
3567 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3568 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3569 ptr2 = strrchrW(source, '\\') + 1;
3570 msiobj_release(&row->hdr);
3572 lstrcpyW(base, package->PackagePath);
3573 ptr = strrchrW(base, '\\');
3574 *(ptr + 1) = '\0';
3576 sourcepath = msi_resolve_file_source(package, file);
3577 ptr = sourcepath + lstrlenW(base);
3578 lstrcpyW(ptr2, ptr);
3579 msi_free(sourcepath);
3581 msi_reg_set_val_str(hkey, squished_pc, source);
3583 RegCloseKey(hkey);
3585 else if (action == INSTALLSTATE_ABSENT)
3587 if (comp->num_clients <= 0)
3589 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3590 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, szLocalSid );
3591 else
3592 MSIREG_DeleteUserDataComponentKey( comp->ComponentId, NULL );
3596 /* UI stuff */
3597 uirow = MSI_CreateRecord(3);
3598 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3599 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3600 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3601 msi_ui_actiondata( package, szProcessComponents, uirow );
3602 msiobj_release( &uirow->hdr );
3604 return ERROR_SUCCESS;
3607 typedef struct {
3608 CLSID clsid;
3609 LPWSTR source;
3611 LPWSTR path;
3612 ITypeLib *ptLib;
3613 } typelib_struct;
3615 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3616 LPWSTR lpszName, LONG_PTR lParam)
3618 TLIBATTR *attr;
3619 typelib_struct *tl_struct = (typelib_struct*) lParam;
3620 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3621 int sz;
3622 HRESULT res;
3624 if (!IS_INTRESOURCE(lpszName))
3626 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3627 return TRUE;
3630 sz = strlenW(tl_struct->source)+4;
3631 sz *= sizeof(WCHAR);
3633 if ((INT_PTR)lpszName == 1)
3634 tl_struct->path = strdupW(tl_struct->source);
3635 else
3637 tl_struct->path = msi_alloc(sz);
3638 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3641 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3642 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3643 if (FAILED(res))
3645 msi_free(tl_struct->path);
3646 tl_struct->path = NULL;
3648 return TRUE;
3651 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3652 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3654 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3655 return FALSE;
3658 msi_free(tl_struct->path);
3659 tl_struct->path = NULL;
3661 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3662 ITypeLib_Release(tl_struct->ptLib);
3664 return TRUE;
3667 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3669 MSIPACKAGE* package = param;
3670 LPCWSTR component;
3671 MSICOMPONENT *comp;
3672 MSIFILE *file;
3673 typelib_struct tl_struct;
3674 ITypeLib *tlib;
3675 HMODULE module;
3676 HRESULT hr;
3678 component = MSI_RecordGetString(row,3);
3679 comp = msi_get_loaded_component(package,component);
3680 if (!comp)
3681 return ERROR_SUCCESS;
3683 comp->Action = msi_get_component_action( package, comp );
3684 if (comp->Action != INSTALLSTATE_LOCAL)
3686 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3687 return ERROR_SUCCESS;
3690 if (!comp->KeyPath || !(file = msi_get_loaded_file( package, comp->KeyPath )))
3692 TRACE("component has no key path\n");
3693 return ERROR_SUCCESS;
3695 msi_ui_actiondata( package, szRegisterTypeLibraries, row );
3697 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3698 if (module)
3700 LPCWSTR guid;
3701 guid = MSI_RecordGetString(row,1);
3702 CLSIDFromString( guid, &tl_struct.clsid);
3703 tl_struct.source = strdupW( file->TargetPath );
3704 tl_struct.path = NULL;
3706 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3707 (LONG_PTR)&tl_struct);
3709 if (tl_struct.path)
3711 LPCWSTR helpid, help_path = NULL;
3712 HRESULT res;
3714 helpid = MSI_RecordGetString(row,6);
3716 if (helpid) help_path = msi_get_target_folder( package, helpid );
3717 res = RegisterTypeLib( tl_struct.ptLib, tl_struct.path, (OLECHAR *)help_path );
3719 if (FAILED(res))
3720 ERR("Failed to register type library %s\n", debugstr_w(tl_struct.path));
3721 else
3722 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3724 ITypeLib_Release(tl_struct.ptLib);
3725 msi_free(tl_struct.path);
3727 else ERR("Failed to load type library %s\n", debugstr_w(tl_struct.source));
3729 FreeLibrary(module);
3730 msi_free(tl_struct.source);
3732 else
3734 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3735 if (FAILED(hr))
3737 ERR("Failed to load type library: %08x\n", hr);
3738 return ERROR_INSTALL_FAILURE;
3741 ITypeLib_Release(tlib);
3744 return ERROR_SUCCESS;
3747 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3749 static const WCHAR query[] = {
3750 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3751 '`','T','y','p','e','L','i','b','`',0};
3752 MSIQUERY *view;
3753 UINT rc;
3755 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3756 if (rc != ERROR_SUCCESS)
3757 return ERROR_SUCCESS;
3759 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3760 msiobj_release(&view->hdr);
3761 return rc;
3764 static UINT ITERATE_UnregisterTypeLibraries( MSIRECORD *row, LPVOID param )
3766 MSIPACKAGE *package = param;
3767 LPCWSTR component, guid;
3768 MSICOMPONENT *comp;
3769 GUID libid;
3770 UINT version;
3771 LCID language;
3772 SYSKIND syskind;
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_ABSENT)
3783 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
3784 return ERROR_SUCCESS;
3786 msi_ui_actiondata( package, szUnregisterTypeLibraries, row );
3788 guid = MSI_RecordGetString( row, 1 );
3789 CLSIDFromString( guid, &libid );
3790 version = MSI_RecordGetInteger( row, 4 );
3791 language = MSI_RecordGetInteger( row, 2 );
3793 #ifdef _WIN64
3794 syskind = SYS_WIN64;
3795 #else
3796 syskind = SYS_WIN32;
3797 #endif
3799 hr = UnRegisterTypeLib( &libid, (version >> 8) & 0xffff, version & 0xff, language, syskind );
3800 if (FAILED(hr))
3802 WARN("Failed to unregister typelib: %08x\n", hr);
3805 return ERROR_SUCCESS;
3808 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
3810 static const WCHAR query[] = {
3811 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3812 '`','T','y','p','e','L','i','b','`',0};
3813 MSIQUERY *view;
3814 UINT rc;
3816 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
3817 if (rc != ERROR_SUCCESS)
3818 return ERROR_SUCCESS;
3820 rc = MSI_IterateRecords( view, NULL, ITERATE_UnregisterTypeLibraries, package );
3821 msiobj_release( &view->hdr );
3822 return rc;
3825 static WCHAR *get_link_file( MSIPACKAGE *package, MSIRECORD *row )
3827 static const WCHAR szlnk[] = {'.','l','n','k',0};
3828 LPCWSTR directory, extension, link_folder;
3829 LPWSTR link_file, filename;
3831 directory = MSI_RecordGetString( row, 2 );
3832 link_folder = msi_get_target_folder( package, directory );
3833 if (!link_folder)
3835 ERR("unable to resolve folder %s\n", debugstr_w(directory));
3836 return NULL;
3838 /* may be needed because of a bug somewhere else */
3839 msi_create_full_path( link_folder );
3841 filename = msi_dup_record_field( row, 3 );
3842 msi_reduce_to_long_filename( filename );
3844 extension = strchrW( filename, '.' );
3845 if (!extension || strcmpiW( extension, szlnk ))
3847 int len = strlenW( filename );
3848 filename = msi_realloc( filename, len * sizeof(WCHAR) + sizeof(szlnk) );
3849 memcpy( filename + len, szlnk, sizeof(szlnk) );
3851 link_file = msi_build_directory_name( 2, link_folder, filename );
3852 msi_free( filename );
3854 return link_file;
3857 WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
3859 static const WCHAR szMicrosoft[] = {'M','i','c','r','o','s','o','f','t','\\',0};
3860 static const WCHAR szInstaller[] = {'I','n','s','t','a','l','l','e','r','\\',0};
3861 WCHAR *folder, *dest, *path;
3863 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3864 folder = msi_dup_property( package->db, szWindowsFolder );
3865 else
3867 WCHAR *appdata = msi_dup_property( package->db, szAppDataFolder );
3868 folder = msi_build_directory_name( 2, appdata, szMicrosoft );
3869 msi_free( appdata );
3871 dest = msi_build_directory_name( 3, folder, szInstaller, package->ProductCode );
3872 msi_create_full_path( dest );
3873 path = msi_build_directory_name( 2, dest, icon_name );
3874 msi_free( folder );
3875 msi_free( dest );
3876 return path;
3879 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3881 MSIPACKAGE *package = param;
3882 LPWSTR link_file, deformated, path;
3883 LPCWSTR component, target;
3884 MSICOMPONENT *comp;
3885 IShellLinkW *sl = NULL;
3886 IPersistFile *pf = NULL;
3887 HRESULT res;
3889 component = MSI_RecordGetString(row, 4);
3890 comp = msi_get_loaded_component(package, component);
3891 if (!comp)
3892 return ERROR_SUCCESS;
3894 comp->Action = msi_get_component_action( package, comp );
3895 if (comp->Action != INSTALLSTATE_LOCAL)
3897 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
3898 return ERROR_SUCCESS;
3900 msi_ui_actiondata( package, szCreateShortcuts, row );
3902 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3903 &IID_IShellLinkW, (LPVOID *) &sl );
3905 if (FAILED( res ))
3907 ERR("CLSID_ShellLink not available\n");
3908 goto err;
3911 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3912 if (FAILED( res ))
3914 ERR("QueryInterface(IID_IPersistFile) failed\n");
3915 goto err;
3918 target = MSI_RecordGetString(row, 5);
3919 if (strchrW(target, '['))
3921 deformat_string( package, target, &path );
3922 TRACE("target path is %s\n", debugstr_w(path));
3923 IShellLinkW_SetPath( sl, path );
3924 msi_free( path );
3926 else
3928 FIXME("poorly handled shortcut format, advertised shortcut\n");
3929 IShellLinkW_SetPath(sl,comp->FullKeypath);
3932 if (!MSI_RecordIsNull(row,6))
3934 LPCWSTR arguments = MSI_RecordGetString(row, 6);
3935 deformat_string(package, arguments, &deformated);
3936 IShellLinkW_SetArguments(sl,deformated);
3937 msi_free(deformated);
3940 if (!MSI_RecordIsNull(row,7))
3942 LPCWSTR description = MSI_RecordGetString(row, 7);
3943 IShellLinkW_SetDescription(sl, description);
3946 if (!MSI_RecordIsNull(row,8))
3947 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3949 if (!MSI_RecordIsNull(row,9))
3951 INT index;
3952 LPCWSTR icon = MSI_RecordGetString(row, 9);
3954 path = msi_build_icon_path(package, icon);
3955 index = MSI_RecordGetInteger(row,10);
3957 /* no value means 0 */
3958 if (index == MSI_NULL_INTEGER)
3959 index = 0;
3961 IShellLinkW_SetIconLocation(sl, path, index);
3962 msi_free(path);
3965 if (!MSI_RecordIsNull(row,11))
3966 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3968 if (!MSI_RecordIsNull(row,12))
3970 LPCWSTR full_path, wkdir = MSI_RecordGetString( row, 12 );
3971 full_path = msi_get_target_folder( package, wkdir );
3972 if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
3974 link_file = get_link_file(package, row);
3976 TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
3977 IPersistFile_Save(pf, link_file, FALSE);
3978 msi_free(link_file);
3980 err:
3981 if (pf)
3982 IPersistFile_Release( pf );
3983 if (sl)
3984 IShellLinkW_Release( sl );
3986 return ERROR_SUCCESS;
3989 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3991 static const WCHAR query[] = {
3992 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3993 '`','S','h','o','r','t','c','u','t','`',0};
3994 MSIQUERY *view;
3995 HRESULT res;
3996 UINT rc;
3998 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
3999 if (rc != ERROR_SUCCESS)
4000 return ERROR_SUCCESS;
4002 res = CoInitialize( NULL );
4004 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
4005 msiobj_release(&view->hdr);
4007 if (SUCCEEDED(res)) CoUninitialize();
4008 return rc;
4011 static UINT ITERATE_RemoveShortcuts( MSIRECORD *row, LPVOID param )
4013 MSIPACKAGE *package = param;
4014 LPWSTR link_file;
4015 LPCWSTR component;
4016 MSICOMPONENT *comp;
4018 component = MSI_RecordGetString( row, 4 );
4019 comp = msi_get_loaded_component( package, component );
4020 if (!comp)
4021 return ERROR_SUCCESS;
4023 comp->Action = msi_get_component_action( package, comp );
4024 if (comp->Action != INSTALLSTATE_ABSENT)
4026 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4027 return ERROR_SUCCESS;
4029 msi_ui_actiondata( package, szRemoveShortcuts, row );
4031 link_file = get_link_file( package, row );
4033 TRACE("Removing shortcut file %s\n", debugstr_w( link_file ));
4034 if (!DeleteFileW( link_file ))
4036 WARN("Failed to remove shortcut file %u\n", GetLastError());
4038 msi_free( link_file );
4040 return ERROR_SUCCESS;
4043 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
4045 static const WCHAR query[] = {
4046 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4047 '`','S','h','o','r','t','c','u','t','`',0};
4048 MSIQUERY *view;
4049 UINT rc;
4051 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4052 if (rc != ERROR_SUCCESS)
4053 return ERROR_SUCCESS;
4055 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveShortcuts, package );
4056 msiobj_release( &view->hdr );
4057 return rc;
4060 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
4062 MSIPACKAGE* package = param;
4063 HANDLE the_file;
4064 LPWSTR FilePath;
4065 LPCWSTR FileName;
4066 CHAR buffer[1024];
4067 DWORD sz;
4068 UINT rc;
4070 FileName = MSI_RecordGetString(row,1);
4071 if (!FileName)
4073 ERR("Unable to get FileName\n");
4074 return ERROR_SUCCESS;
4077 FilePath = msi_build_icon_path(package, FileName);
4079 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
4081 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
4082 FILE_ATTRIBUTE_NORMAL, NULL);
4084 if (the_file == INVALID_HANDLE_VALUE)
4086 ERR("Unable to create file %s\n",debugstr_w(FilePath));
4087 msi_free(FilePath);
4088 return ERROR_SUCCESS;
4093 DWORD write;
4094 sz = 1024;
4095 rc = MSI_RecordReadStream(row,2,buffer,&sz);
4096 if (rc != ERROR_SUCCESS)
4098 ERR("Failed to get stream\n");
4099 CloseHandle(the_file);
4100 DeleteFileW(FilePath);
4101 break;
4103 WriteFile(the_file,buffer,sz,&write,NULL);
4104 } while (sz == 1024);
4106 msi_free(FilePath);
4107 CloseHandle(the_file);
4109 return ERROR_SUCCESS;
4112 static UINT msi_publish_icons(MSIPACKAGE *package)
4114 static const WCHAR query[]= {
4115 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4116 '`','I','c','o','n','`',0};
4117 MSIQUERY *view;
4118 UINT r;
4120 r = MSI_DatabaseOpenViewW(package->db, query, &view);
4121 if (r == ERROR_SUCCESS)
4123 r = MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
4124 msiobj_release(&view->hdr);
4125 if (r != ERROR_SUCCESS)
4126 return r;
4128 return ERROR_SUCCESS;
4131 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
4133 UINT r;
4134 HKEY source;
4135 LPWSTR buffer;
4136 MSIMEDIADISK *disk;
4137 MSISOURCELISTINFO *info;
4139 r = RegCreateKeyW(hkey, szSourceList, &source);
4140 if (r != ERROR_SUCCESS)
4141 return r;
4143 RegCloseKey(source);
4145 buffer = strrchrW(package->PackagePath, '\\') + 1;
4146 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4147 package->Context, MSICODE_PRODUCT,
4148 INSTALLPROPERTY_PACKAGENAMEW, buffer);
4149 if (r != ERROR_SUCCESS)
4150 return r;
4152 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4153 package->Context, MSICODE_PRODUCT,
4154 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
4155 if (r != ERROR_SUCCESS)
4156 return r;
4158 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
4159 package->Context, MSICODE_PRODUCT,
4160 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
4161 if (r != ERROR_SUCCESS)
4162 return r;
4164 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
4166 if (!strcmpW( info->property, INSTALLPROPERTY_LASTUSEDSOURCEW ))
4167 msi_set_last_used_source(package->ProductCode, NULL, info->context,
4168 info->options, info->value);
4169 else
4170 MsiSourceListSetInfoW(package->ProductCode, NULL,
4171 info->context, info->options,
4172 info->property, info->value);
4175 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
4177 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
4178 disk->context, disk->options,
4179 disk->disk_id, disk->volume_label, disk->disk_prompt);
4182 return ERROR_SUCCESS;
4185 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
4187 MSIHANDLE hdb, suminfo;
4188 WCHAR guids[MAX_PATH];
4189 WCHAR packcode[SQUISH_GUID_SIZE];
4190 LPWSTR buffer;
4191 LPWSTR ptr;
4192 DWORD langid;
4193 DWORD size;
4194 UINT r;
4196 static const WCHAR szARPProductIcon[] =
4197 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
4198 static const WCHAR szAssignment[] =
4199 {'A','s','s','i','g','n','m','e','n','t',0};
4200 static const WCHAR szAdvertiseFlags[] =
4201 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
4202 static const WCHAR szClients[] =
4203 {'C','l','i','e','n','t','s',0};
4204 static const WCHAR szColon[] = {':',0};
4206 buffer = msi_dup_property(package->db, INSTALLPROPERTY_PRODUCTNAMEW);
4207 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
4208 msi_free(buffer);
4210 langid = msi_get_property_int(package->db, szProductLanguage, 0);
4211 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4213 /* FIXME */
4214 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
4216 buffer = msi_dup_property(package->db, szARPProductIcon);
4217 if (buffer)
4219 LPWSTR path = msi_build_icon_path(package, buffer);
4220 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
4221 msi_free(path);
4222 msi_free(buffer);
4225 buffer = msi_dup_property(package->db, szProductVersion);
4226 if (buffer)
4228 DWORD verdword = msi_version_str_to_dword(buffer);
4229 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4230 msi_free(buffer);
4233 msi_reg_set_val_dword(hkey, szAssignment, 0);
4234 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
4235 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
4236 msi_reg_set_val_str(hkey, szClients, szColon);
4238 hdb = alloc_msihandle(&package->db->hdr);
4239 if (!hdb)
4240 return ERROR_NOT_ENOUGH_MEMORY;
4242 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
4243 MsiCloseHandle(hdb);
4244 if (r != ERROR_SUCCESS)
4245 goto done;
4247 size = MAX_PATH;
4248 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
4249 NULL, guids, &size);
4250 if (r != ERROR_SUCCESS)
4251 goto done;
4253 ptr = strchrW(guids, ';');
4254 if (ptr) *ptr = 0;
4255 squash_guid(guids, packcode);
4256 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
4258 done:
4259 MsiCloseHandle(suminfo);
4260 return ERROR_SUCCESS;
4263 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
4265 UINT r;
4266 HKEY hkey;
4267 LPWSTR upgrade;
4268 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4270 upgrade = msi_dup_property(package->db, szUpgradeCode);
4271 if (!upgrade)
4272 return ERROR_SUCCESS;
4274 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4275 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
4276 else
4277 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
4279 if (r != ERROR_SUCCESS)
4281 WARN("failed to open upgrade code key\n");
4282 msi_free(upgrade);
4283 return ERROR_SUCCESS;
4285 squash_guid(package->ProductCode, squashed_pc);
4286 msi_reg_set_val_str(hkey, squashed_pc, NULL);
4287 RegCloseKey(hkey);
4288 msi_free(upgrade);
4289 return ERROR_SUCCESS;
4292 static BOOL msi_check_publish(MSIPACKAGE *package)
4294 MSIFEATURE *feature;
4296 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4298 feature->Action = msi_get_feature_action( package, feature );
4299 if (feature->Action == INSTALLSTATE_LOCAL)
4300 return TRUE;
4303 return FALSE;
4306 static BOOL msi_check_unpublish(MSIPACKAGE *package)
4308 MSIFEATURE *feature;
4310 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4312 feature->Action = msi_get_feature_action( package, feature );
4313 if (feature->Action != INSTALLSTATE_ABSENT)
4314 return FALSE;
4317 return TRUE;
4320 static UINT msi_publish_patches( MSIPACKAGE *package )
4322 static const WCHAR szAllPatches[] = {'A','l','l','P','a','t','c','h','e','s',0};
4323 WCHAR patch_squashed[GUID_SIZE];
4324 HKEY patches_key = NULL, product_patches_key = NULL, product_key;
4325 LONG res;
4326 MSIPATCHINFO *patch;
4327 UINT r;
4328 WCHAR *p, *all_patches = NULL;
4329 DWORD len = 0;
4331 r = MSIREG_OpenProductKey( package->ProductCode, NULL, package->Context, &product_key, TRUE );
4332 if (r != ERROR_SUCCESS)
4333 return ERROR_FUNCTION_FAILED;
4335 res = RegCreateKeyExW( product_key, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patches_key, NULL );
4336 if (res != ERROR_SUCCESS)
4338 r = ERROR_FUNCTION_FAILED;
4339 goto done;
4342 r = MSIREG_OpenUserDataProductPatchesKey( package->ProductCode, package->Context, &product_patches_key, TRUE );
4343 if (r != ERROR_SUCCESS)
4344 goto done;
4346 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4348 squash_guid( patch->patchcode, patch_squashed );
4349 len += strlenW( patch_squashed ) + 1;
4352 p = all_patches = msi_alloc( (len + 1) * sizeof(WCHAR) );
4353 if (!all_patches)
4354 goto done;
4356 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
4358 HKEY patch_key;
4360 squash_guid( patch->patchcode, p );
4361 p += strlenW( p ) + 1;
4363 res = RegSetValueExW( patches_key, patch_squashed, 0, REG_SZ,
4364 (const BYTE *)patch->transforms,
4365 (strlenW(patch->transforms) + 1) * sizeof(WCHAR) );
4366 if (res != ERROR_SUCCESS)
4367 goto done;
4369 r = MSIREG_OpenUserDataPatchKey( patch->patchcode, package->Context, &patch_key, TRUE );
4370 if (r != ERROR_SUCCESS)
4371 goto done;
4373 res = RegSetValueExW( patch_key, szLocalPackage, 0, REG_SZ, (const BYTE *)patch->localfile,
4374 (strlenW( patch->localfile ) + 1) * sizeof(WCHAR) );
4375 RegCloseKey( patch_key );
4376 if (res != ERROR_SUCCESS)
4377 goto done;
4379 if (patch->filename && !CopyFileW( patch->filename, patch->localfile, FALSE ))
4381 res = GetLastError();
4382 ERR("Unable to copy patch package %d\n", res);
4383 goto done;
4385 res = RegCreateKeyExW( product_patches_key, patch_squashed, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &patch_key, NULL );
4386 if (res != ERROR_SUCCESS)
4387 goto done;
4389 res = RegSetValueExW( patch_key, szState, 0, REG_DWORD, (const BYTE *)&patch->state, sizeof(patch->state) );
4390 RegCloseKey( patch_key );
4391 if (res != ERROR_SUCCESS)
4392 goto done;
4395 all_patches[len] = 0;
4396 res = RegSetValueExW( patches_key, szPatches, 0, REG_MULTI_SZ,
4397 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4398 if (res != ERROR_SUCCESS)
4399 goto done;
4401 res = RegSetValueExW( product_patches_key, szAllPatches, 0, REG_MULTI_SZ,
4402 (const BYTE *)all_patches, (len + 1) * sizeof(WCHAR) );
4403 if (res != ERROR_SUCCESS)
4404 r = ERROR_FUNCTION_FAILED;
4406 done:
4407 RegCloseKey( product_patches_key );
4408 RegCloseKey( patches_key );
4409 RegCloseKey( product_key );
4410 msi_free( all_patches );
4411 return r;
4414 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
4416 UINT rc;
4417 HKEY hukey = NULL, hudkey = NULL;
4418 MSIRECORD *uirow;
4420 if (!list_empty(&package->patches))
4422 rc = msi_publish_patches(package);
4423 if (rc != ERROR_SUCCESS)
4424 goto end;
4427 /* FIXME: also need to publish if the product is in advertise mode */
4428 if (!msi_check_publish(package))
4429 return ERROR_SUCCESS;
4431 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
4432 &hukey, TRUE);
4433 if (rc != ERROR_SUCCESS)
4434 goto end;
4436 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
4437 NULL, &hudkey, TRUE);
4438 if (rc != ERROR_SUCCESS)
4439 goto end;
4441 rc = msi_publish_upgrade_code(package);
4442 if (rc != ERROR_SUCCESS)
4443 goto end;
4445 rc = msi_publish_product_properties(package, hukey);
4446 if (rc != ERROR_SUCCESS)
4447 goto end;
4449 rc = msi_publish_sourcelist(package, hukey);
4450 if (rc != ERROR_SUCCESS)
4451 goto end;
4453 rc = msi_publish_icons(package);
4455 end:
4456 uirow = MSI_CreateRecord( 1 );
4457 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
4458 msi_ui_actiondata( package, szPublishProduct, uirow );
4459 msiobj_release( &uirow->hdr );
4461 RegCloseKey(hukey);
4462 RegCloseKey(hudkey);
4463 return rc;
4466 static WCHAR *get_ini_file_name( MSIPACKAGE *package, MSIRECORD *row )
4468 WCHAR *filename, *ptr, *folder, *ret;
4469 const WCHAR *dirprop;
4471 filename = msi_dup_record_field( row, 2 );
4472 if (filename && (ptr = strchrW( filename, '|' )))
4473 ptr++;
4474 else
4475 ptr = filename;
4477 dirprop = MSI_RecordGetString( row, 3 );
4478 if (dirprop)
4480 folder = strdupW( msi_get_target_folder( package, dirprop ) );
4481 if (!folder) folder = msi_dup_property( package->db, dirprop );
4483 else
4484 folder = msi_dup_property( package->db, szWindowsFolder );
4486 if (!folder)
4488 ERR("Unable to resolve folder %s\n", debugstr_w(dirprop));
4489 msi_free( filename );
4490 return NULL;
4493 ret = msi_build_directory_name( 2, folder, ptr );
4495 msi_free( filename );
4496 msi_free( folder );
4497 return ret;
4500 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
4502 MSIPACKAGE *package = param;
4503 LPCWSTR component, section, key, value, identifier;
4504 LPWSTR deformated_section, deformated_key, deformated_value, fullname;
4505 MSIRECORD * uirow;
4506 INT action;
4507 MSICOMPONENT *comp;
4509 component = MSI_RecordGetString(row, 8);
4510 comp = msi_get_loaded_component(package,component);
4511 if (!comp)
4512 return ERROR_SUCCESS;
4514 comp->Action = msi_get_component_action( package, comp );
4515 if (comp->Action != INSTALLSTATE_LOCAL)
4517 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4518 return ERROR_SUCCESS;
4521 identifier = MSI_RecordGetString(row,1);
4522 section = MSI_RecordGetString(row,4);
4523 key = MSI_RecordGetString(row,5);
4524 value = MSI_RecordGetString(row,6);
4525 action = MSI_RecordGetInteger(row,7);
4527 deformat_string(package,section,&deformated_section);
4528 deformat_string(package,key,&deformated_key);
4529 deformat_string(package,value,&deformated_value);
4531 fullname = get_ini_file_name(package, row);
4533 if (action == 0)
4535 TRACE("Adding value %s to section %s in %s\n",
4536 debugstr_w(deformated_key), debugstr_w(deformated_section),
4537 debugstr_w(fullname));
4538 WritePrivateProfileStringW(deformated_section, deformated_key,
4539 deformated_value, fullname);
4541 else if (action == 1)
4543 WCHAR returned[10];
4544 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
4545 returned, 10, fullname);
4546 if (returned[0] == 0)
4548 TRACE("Adding value %s to section %s in %s\n",
4549 debugstr_w(deformated_key), debugstr_w(deformated_section),
4550 debugstr_w(fullname));
4552 WritePrivateProfileStringW(deformated_section, deformated_key,
4553 deformated_value, fullname);
4556 else if (action == 3)
4557 FIXME("Append to existing section not yet implemented\n");
4559 uirow = MSI_CreateRecord(4);
4560 MSI_RecordSetStringW(uirow,1,identifier);
4561 MSI_RecordSetStringW(uirow,2,deformated_section);
4562 MSI_RecordSetStringW(uirow,3,deformated_key);
4563 MSI_RecordSetStringW(uirow,4,deformated_value);
4564 msi_ui_actiondata( package, szWriteIniValues, uirow );
4565 msiobj_release( &uirow->hdr );
4567 msi_free(fullname);
4568 msi_free(deformated_key);
4569 msi_free(deformated_value);
4570 msi_free(deformated_section);
4571 return ERROR_SUCCESS;
4574 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
4576 static const WCHAR query[] = {
4577 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4578 '`','I','n','i','F','i','l','e','`',0};
4579 MSIQUERY *view;
4580 UINT rc;
4582 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4583 if (rc != ERROR_SUCCESS)
4584 return ERROR_SUCCESS;
4586 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
4587 msiobj_release(&view->hdr);
4588 return rc;
4591 static UINT ITERATE_RemoveIniValuesOnUninstall( MSIRECORD *row, LPVOID param )
4593 MSIPACKAGE *package = param;
4594 LPCWSTR component, section, key, value, identifier;
4595 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4596 MSICOMPONENT *comp;
4597 MSIRECORD *uirow;
4598 INT action;
4600 component = MSI_RecordGetString( row, 8 );
4601 comp = msi_get_loaded_component( package, component );
4602 if (!comp)
4603 return ERROR_SUCCESS;
4605 comp->Action = msi_get_component_action( package, comp );
4606 if (comp->Action != INSTALLSTATE_ABSENT)
4608 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
4609 return ERROR_SUCCESS;
4612 identifier = MSI_RecordGetString( row, 1 );
4613 section = MSI_RecordGetString( row, 4 );
4614 key = MSI_RecordGetString( row, 5 );
4615 value = MSI_RecordGetString( row, 6 );
4616 action = MSI_RecordGetInteger( row, 7 );
4618 deformat_string( package, section, &deformated_section );
4619 deformat_string( package, key, &deformated_key );
4620 deformat_string( package, value, &deformated_value );
4622 if (action == msidbIniFileActionAddLine || action == msidbIniFileActionCreateLine)
4624 filename = get_ini_file_name( package, row );
4626 TRACE("Removing key %s from section %s in %s\n",
4627 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4629 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4631 WARN("Unable to remove key %u\n", GetLastError());
4633 msi_free( filename );
4635 else
4636 FIXME("Unsupported action %d\n", action);
4639 uirow = MSI_CreateRecord( 4 );
4640 MSI_RecordSetStringW( uirow, 1, identifier );
4641 MSI_RecordSetStringW( uirow, 2, deformated_section );
4642 MSI_RecordSetStringW( uirow, 3, deformated_key );
4643 MSI_RecordSetStringW( uirow, 4, deformated_value );
4644 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4645 msiobj_release( &uirow->hdr );
4647 msi_free( deformated_key );
4648 msi_free( deformated_value );
4649 msi_free( deformated_section );
4650 return ERROR_SUCCESS;
4653 static UINT ITERATE_RemoveIniValuesOnInstall( MSIRECORD *row, LPVOID param )
4655 MSIPACKAGE *package = param;
4656 LPCWSTR component, section, key, value, identifier;
4657 LPWSTR deformated_section, deformated_key, deformated_value, filename;
4658 MSICOMPONENT *comp;
4659 MSIRECORD *uirow;
4660 INT action;
4662 component = MSI_RecordGetString( row, 8 );
4663 comp = msi_get_loaded_component( package, component );
4664 if (!comp)
4665 return ERROR_SUCCESS;
4667 comp->Action = msi_get_component_action( package, comp );
4668 if (comp->Action != INSTALLSTATE_LOCAL)
4670 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
4671 return ERROR_SUCCESS;
4674 identifier = MSI_RecordGetString( row, 1 );
4675 section = MSI_RecordGetString( row, 4 );
4676 key = MSI_RecordGetString( row, 5 );
4677 value = MSI_RecordGetString( row, 6 );
4678 action = MSI_RecordGetInteger( row, 7 );
4680 deformat_string( package, section, &deformated_section );
4681 deformat_string( package, key, &deformated_key );
4682 deformat_string( package, value, &deformated_value );
4684 if (action == msidbIniFileActionRemoveLine)
4686 filename = get_ini_file_name( package, row );
4688 TRACE("Removing key %s from section %s in %s\n",
4689 debugstr_w(deformated_key), debugstr_w(deformated_section), debugstr_w(filename));
4691 if (!WritePrivateProfileStringW( deformated_section, deformated_key, NULL, filename ))
4693 WARN("Unable to remove key %u\n", GetLastError());
4695 msi_free( filename );
4697 else
4698 FIXME("Unsupported action %d\n", action);
4700 uirow = MSI_CreateRecord( 4 );
4701 MSI_RecordSetStringW( uirow, 1, identifier );
4702 MSI_RecordSetStringW( uirow, 2, deformated_section );
4703 MSI_RecordSetStringW( uirow, 3, deformated_key );
4704 MSI_RecordSetStringW( uirow, 4, deformated_value );
4705 msi_ui_actiondata( package, szRemoveIniValues, uirow );
4706 msiobj_release( &uirow->hdr );
4708 msi_free( deformated_key );
4709 msi_free( deformated_value );
4710 msi_free( deformated_section );
4711 return ERROR_SUCCESS;
4714 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
4716 static const WCHAR query[] = {
4717 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4718 '`','I','n','i','F','i','l','e','`',0};
4719 static const WCHAR remove_query[] = {
4720 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4721 '`','R','e','m','o','v','e','I','n','i','F','i','l','e','`',0};
4722 MSIQUERY *view;
4723 UINT rc;
4725 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4726 if (rc == ERROR_SUCCESS)
4728 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnUninstall, package );
4729 msiobj_release( &view->hdr );
4730 if (rc != ERROR_SUCCESS)
4731 return rc;
4733 rc = MSI_DatabaseOpenViewW( package->db, remove_query, &view );
4734 if (rc == ERROR_SUCCESS)
4736 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveIniValuesOnInstall, package );
4737 msiobj_release( &view->hdr );
4738 if (rc != ERROR_SUCCESS)
4739 return rc;
4741 return ERROR_SUCCESS;
4744 static void register_dll( const WCHAR *dll, BOOL unregister )
4746 static const WCHAR regW[] =
4747 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"','%','s','\"',0};
4748 static const WCHAR unregW[] =
4749 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','/','u',' ','\"','%','s','\"',0};
4750 PROCESS_INFORMATION pi;
4751 STARTUPINFOW si;
4752 WCHAR *cmd;
4754 if (!(cmd = msi_alloc( strlenW(dll) * sizeof(WCHAR) + sizeof(unregW) ))) return;
4756 if (unregister) sprintfW( cmd, unregW, dll );
4757 else sprintfW( cmd, regW, dll );
4759 memset( &si, 0, sizeof(STARTUPINFOW) );
4760 if (CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ))
4762 CloseHandle( pi.hThread );
4763 msi_dialog_check_messages( pi.hProcess );
4764 CloseHandle( pi.hProcess );
4766 msi_free( cmd );
4769 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
4771 MSIPACKAGE *package = param;
4772 LPCWSTR filename;
4773 MSIFILE *file;
4774 MSIRECORD *uirow;
4776 filename = MSI_RecordGetString( row, 1 );
4777 file = msi_get_loaded_file( package, filename );
4778 if (!file)
4780 WARN("unable to find file %s\n", debugstr_w(filename));
4781 return ERROR_SUCCESS;
4783 file->Component->Action = msi_get_component_action( package, file->Component );
4784 if (file->Component->Action != INSTALLSTATE_LOCAL)
4786 TRACE("component not scheduled for installation %s\n", debugstr_w(file->Component->Component));
4787 return ERROR_SUCCESS;
4790 TRACE("Registering %s\n", debugstr_w( file->TargetPath ));
4791 register_dll( file->TargetPath, FALSE );
4793 uirow = MSI_CreateRecord( 2 );
4794 MSI_RecordSetStringW( uirow, 1, file->File );
4795 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4796 msi_ui_actiondata( package, szSelfRegModules, uirow );
4797 msiobj_release( &uirow->hdr );
4799 return ERROR_SUCCESS;
4802 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
4804 static const WCHAR query[] = {
4805 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4806 '`','S','e','l','f','R','e','g','`',0};
4807 MSIQUERY *view;
4808 UINT rc;
4810 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4811 if (rc != ERROR_SUCCESS)
4812 return ERROR_SUCCESS;
4814 rc = MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
4815 msiobj_release(&view->hdr);
4816 return rc;
4819 static UINT ITERATE_SelfUnregModules( MSIRECORD *row, LPVOID param )
4821 MSIPACKAGE *package = param;
4822 LPCWSTR filename;
4823 MSIFILE *file;
4824 MSIRECORD *uirow;
4826 filename = MSI_RecordGetString( row, 1 );
4827 file = msi_get_loaded_file( package, filename );
4828 if (!file)
4830 WARN("unable to find file %s\n", debugstr_w(filename));
4831 return ERROR_SUCCESS;
4833 file->Component->Action = msi_get_component_action( package, file->Component );
4834 if (file->Component->Action != INSTALLSTATE_ABSENT)
4836 TRACE("component not scheduled for removal %s\n", debugstr_w(file->Component->Component));
4837 return ERROR_SUCCESS;
4840 TRACE("Unregistering %s\n", debugstr_w( file->TargetPath ));
4841 register_dll( file->TargetPath, TRUE );
4843 uirow = MSI_CreateRecord( 2 );
4844 MSI_RecordSetStringW( uirow, 1, file->File );
4845 MSI_RecordSetStringW( uirow, 2, file->Component->Directory );
4846 msi_ui_actiondata( package, szSelfUnregModules, uirow );
4847 msiobj_release( &uirow->hdr );
4849 return ERROR_SUCCESS;
4852 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
4854 static const WCHAR query[] = {
4855 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4856 '`','S','e','l','f','R','e','g','`',0};
4857 MSIQUERY *view;
4858 UINT rc;
4860 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
4861 if (rc != ERROR_SUCCESS)
4862 return ERROR_SUCCESS;
4864 rc = MSI_IterateRecords( view, NULL, ITERATE_SelfUnregModules, package );
4865 msiobj_release( &view->hdr );
4866 return rc;
4869 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
4871 MSIFEATURE *feature;
4872 UINT rc;
4873 HKEY hkey = NULL, userdata = NULL;
4875 if (!msi_check_publish(package))
4876 return ERROR_SUCCESS;
4878 rc = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4879 &hkey, TRUE);
4880 if (rc != ERROR_SUCCESS)
4881 goto end;
4883 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4884 &userdata, TRUE);
4885 if (rc != ERROR_SUCCESS)
4886 goto end;
4888 /* here the guids are base 85 encoded */
4889 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
4891 ComponentList *cl;
4892 LPWSTR data = NULL;
4893 GUID clsid;
4894 INT size;
4895 BOOL absent = FALSE;
4896 MSIRECORD *uirow;
4898 if (feature->Action != INSTALLSTATE_LOCAL &&
4899 feature->Action != INSTALLSTATE_SOURCE &&
4900 feature->Action != INSTALLSTATE_ADVERTISED) absent = TRUE;
4902 size = 1;
4903 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4905 size += 21;
4907 if (feature->Feature_Parent)
4908 size += strlenW( feature->Feature_Parent )+2;
4910 data = msi_alloc(size * sizeof(WCHAR));
4912 data[0] = 0;
4913 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4915 MSICOMPONENT* component = cl->component;
4916 WCHAR buf[21];
4918 buf[0] = 0;
4919 if (component->ComponentId)
4921 TRACE("From %s\n",debugstr_w(component->ComponentId));
4922 CLSIDFromString(component->ComponentId, &clsid);
4923 encode_base85_guid(&clsid,buf);
4924 TRACE("to %s\n",debugstr_w(buf));
4925 strcatW(data,buf);
4929 if (feature->Feature_Parent)
4931 static const WCHAR sep[] = {'\2',0};
4932 strcatW(data,sep);
4933 strcatW(data,feature->Feature_Parent);
4936 msi_reg_set_val_str( userdata, feature->Feature, data );
4937 msi_free(data);
4939 size = 0;
4940 if (feature->Feature_Parent)
4941 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4942 if (!absent)
4944 size += sizeof(WCHAR);
4945 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4946 (const BYTE*)(feature->Feature_Parent ? feature->Feature_Parent : szEmpty),size);
4948 else
4950 size += 2*sizeof(WCHAR);
4951 data = msi_alloc(size);
4952 data[0] = 0x6;
4953 data[1] = 0;
4954 if (feature->Feature_Parent)
4955 strcpyW( &data[1], feature->Feature_Parent );
4956 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4957 (LPBYTE)data,size);
4958 msi_free(data);
4961 /* the UI chunk */
4962 uirow = MSI_CreateRecord( 1 );
4963 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4964 msi_ui_actiondata( package, szPublishFeatures, uirow );
4965 msiobj_release( &uirow->hdr );
4966 /* FIXME: call msi_ui_progress? */
4969 end:
4970 RegCloseKey(hkey);
4971 RegCloseKey(userdata);
4972 return rc;
4975 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4977 UINT r;
4978 HKEY hkey;
4979 MSIRECORD *uirow;
4981 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4983 r = MSIREG_OpenFeaturesKey(package->ProductCode, NULL, package->Context,
4984 &hkey, FALSE);
4985 if (r == ERROR_SUCCESS)
4987 RegDeleteValueW(hkey, feature->Feature);
4988 RegCloseKey(hkey);
4991 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, NULL, package->Context,
4992 &hkey, FALSE);
4993 if (r == ERROR_SUCCESS)
4995 RegDeleteValueW(hkey, feature->Feature);
4996 RegCloseKey(hkey);
4999 uirow = MSI_CreateRecord( 1 );
5000 MSI_RecordSetStringW( uirow, 1, feature->Feature );
5001 msi_ui_actiondata( package, szUnpublishFeatures, uirow );
5002 msiobj_release( &uirow->hdr );
5004 return ERROR_SUCCESS;
5007 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
5009 MSIFEATURE *feature;
5011 if (!msi_check_unpublish(package))
5012 return ERROR_SUCCESS;
5014 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
5016 msi_unpublish_feature(package, feature);
5019 return ERROR_SUCCESS;
5022 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
5024 SYSTEMTIME systime;
5025 DWORD size, langid;
5026 WCHAR date[9], *val, *buffer;
5027 const WCHAR *prop, *key;
5029 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
5030 static const WCHAR modpath_fmt[] =
5031 {'M','s','i','E','x','e','c','.','e','x','e',' ',
5032 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
5033 static const WCHAR szModifyPath[] =
5034 {'M','o','d','i','f','y','P','a','t','h',0};
5035 static const WCHAR szUninstallString[] =
5036 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
5037 static const WCHAR szEstimatedSize[] =
5038 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
5039 static const WCHAR szDisplayVersion[] =
5040 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
5041 static const WCHAR szInstallSource[] =
5042 {'I','n','s','t','a','l','l','S','o','u','r','c','e',0};
5043 static const WCHAR szARPAUTHORIZEDCDFPREFIX[] =
5044 {'A','R','P','A','U','T','H','O','R','I','Z','E','D','C','D','F','P','R','E','F','I','X',0};
5045 static const WCHAR szAuthorizedCDFPrefix[] =
5046 {'A','u','t','h','o','r','i','z','e','d','C','D','F','P','r','e','f','i','x',0};
5047 static const WCHAR szARPCONTACT[] =
5048 {'A','R','P','C','O','N','T','A','C','T',0};
5049 static const WCHAR szContact[] =
5050 {'C','o','n','t','a','c','t',0};
5051 static const WCHAR szARPCOMMENTS[] =
5052 {'A','R','P','C','O','M','M','E','N','T','S',0};
5053 static const WCHAR szComments[] =
5054 {'C','o','m','m','e','n','t','s',0};
5055 static const WCHAR szProductName[] =
5056 {'P','r','o','d','u','c','t','N','a','m','e',0};
5057 static const WCHAR szDisplayName[] =
5058 {'D','i','s','p','l','a','y','N','a','m','e',0};
5059 static const WCHAR szARPHELPLINK[] =
5060 {'A','R','P','H','E','L','P','L','I','N','K',0};
5061 static const WCHAR szHelpLink[] =
5062 {'H','e','l','p','L','i','n','k',0};
5063 static const WCHAR szARPHELPTELEPHONE[] =
5064 {'A','R','P','H','E','L','P','T','E','L','E','P','H','O','N','E',0};
5065 static const WCHAR szHelpTelephone[] =
5066 {'H','e','l','p','T','e','l','e','p','h','o','n','e',0};
5067 static const WCHAR szARPINSTALLLOCATION[] =
5068 {'A','R','P','I','N','S','T','A','L','L','L','O','C','A','T','I','O','N',0};
5069 static const WCHAR szManufacturer[] =
5070 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
5071 static const WCHAR szPublisher[] =
5072 {'P','u','b','l','i','s','h','e','r',0};
5073 static const WCHAR szARPREADME[] =
5074 {'A','R','P','R','E','A','D','M','E',0};
5075 static const WCHAR szReadme[] =
5076 {'R','e','a','d','M','e',0};
5077 static const WCHAR szARPSIZE[] =
5078 {'A','R','P','S','I','Z','E',0};
5079 static const WCHAR szSize[] =
5080 {'S','i','z','e',0};
5081 static const WCHAR szARPURLINFOABOUT[] =
5082 {'A','R','P','U','R','L','I','N','F','O','A','B','O','U','T',0};
5083 static const WCHAR szURLInfoAbout[] =
5084 {'U','R','L','I','n','f','o','A','b','o','u','t',0};
5085 static const WCHAR szARPURLUPDATEINFO[] =
5086 {'A','R','P','U','R','L','U','P','D','A','T','E','I','N','F','O',0};
5087 static const WCHAR szURLUpdateInfo[] =
5088 {'U','R','L','U','p','d','a','t','e','I','n','f','o',0};
5089 static const WCHAR szARPSYSTEMCOMPONENT[] =
5090 {'A','R','P','S','Y','S','T','E','M','C','O','M','P','O','N','E','N','T',0};
5091 static const WCHAR szSystemComponent[] =
5092 {'S','y','s','t','e','m','C','o','m','p','o','n','e','n','t',0};
5094 static const WCHAR *propval[] = {
5095 szARPAUTHORIZEDCDFPREFIX, szAuthorizedCDFPrefix,
5096 szARPCONTACT, szContact,
5097 szARPCOMMENTS, szComments,
5098 szProductName, szDisplayName,
5099 szARPHELPLINK, szHelpLink,
5100 szARPHELPTELEPHONE, szHelpTelephone,
5101 szARPINSTALLLOCATION, szInstallLocation,
5102 szSourceDir, szInstallSource,
5103 szManufacturer, szPublisher,
5104 szARPREADME, szReadme,
5105 szARPSIZE, szSize,
5106 szARPURLINFOABOUT, szURLInfoAbout,
5107 szARPURLUPDATEINFO, szURLUpdateInfo,
5108 NULL
5110 const WCHAR **p = propval;
5112 while (*p)
5114 prop = *p++;
5115 key = *p++;
5116 val = msi_dup_property(package->db, prop);
5117 msi_reg_set_val_str(hkey, key, val);
5118 msi_free(val);
5121 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
5122 if (msi_get_property_int( package->db, szARPSYSTEMCOMPONENT, 0 ))
5124 msi_reg_set_val_dword( hkey, szSystemComponent, 1 );
5126 size = deformat_string(package, modpath_fmt, &buffer) * sizeof(WCHAR);
5127 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5128 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
5129 msi_free(buffer);
5131 /* FIXME: Write real Estimated Size when we have it */
5132 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
5134 GetLocalTime(&systime);
5135 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
5136 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
5138 langid = msi_get_property_int(package->db, szProductLanguage, 0);
5139 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
5141 buffer = msi_dup_property(package->db, szProductVersion);
5142 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
5143 if (buffer)
5145 DWORD verdword = msi_version_str_to_dword(buffer);
5147 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
5148 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
5149 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
5150 msi_free(buffer);
5153 return ERROR_SUCCESS;
5156 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
5158 WCHAR squashed_pc[SQUISH_GUID_SIZE];
5159 MSIRECORD *uirow;
5160 LPWSTR upgrade_code;
5161 HKEY hkey, props, upgrade_key;
5162 UINT rc;
5164 /* FIXME: also need to publish if the product is in advertise mode */
5165 if (!msi_check_publish(package))
5166 return ERROR_SUCCESS;
5168 rc = MSIREG_OpenUninstallKey(package->ProductCode, package->platform, &hkey, TRUE);
5169 if (rc != ERROR_SUCCESS)
5170 return rc;
5172 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context, NULL, &props, TRUE);
5173 if (rc != ERROR_SUCCESS)
5174 goto done;
5176 rc = msi_publish_install_properties(package, hkey);
5177 if (rc != ERROR_SUCCESS)
5178 goto done;
5180 rc = msi_publish_install_properties(package, props);
5181 if (rc != ERROR_SUCCESS)
5182 goto done;
5184 upgrade_code = msi_dup_property(package->db, szUpgradeCode);
5185 if (upgrade_code)
5187 rc = MSIREG_OpenUpgradeCodesKey( upgrade_code, &upgrade_key, TRUE );
5188 if (rc == ERROR_SUCCESS)
5190 squash_guid( package->ProductCode, squashed_pc );
5191 msi_reg_set_val_str( upgrade_key, squashed_pc, NULL );
5192 RegCloseKey( upgrade_key );
5194 msi_free( upgrade_code );
5196 msi_reg_set_val_str( props, INSTALLPROPERTY_LOCALPACKAGEW, package->localfile );
5197 package->delete_on_close = FALSE;
5199 done:
5200 uirow = MSI_CreateRecord( 1 );
5201 MSI_RecordSetStringW( uirow, 1, package->ProductCode );
5202 msi_ui_actiondata( package, szRegisterProduct, uirow );
5203 msiobj_release( &uirow->hdr );
5205 RegCloseKey(hkey);
5206 return ERROR_SUCCESS;
5209 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
5211 return execute_script(package, SCRIPT_INSTALL);
5214 static UINT ITERATE_UnpublishIcon( MSIRECORD *row, LPVOID param )
5216 MSIPACKAGE *package = param;
5217 const WCHAR *icon = MSI_RecordGetString( row, 1 );
5218 WCHAR *p, *icon_path;
5220 if (!icon) return ERROR_SUCCESS;
5221 if ((icon_path = msi_build_icon_path( package, icon )))
5223 TRACE("removing icon file %s\n", debugstr_w(icon_path));
5224 DeleteFileW( icon_path );
5225 if ((p = strrchrW( icon_path, '\\' )))
5227 *p = 0;
5228 RemoveDirectoryW( icon_path );
5230 msi_free( icon_path );
5232 return ERROR_SUCCESS;
5235 static UINT msi_unpublish_icons( MSIPACKAGE *package )
5237 static const WCHAR query[]= {
5238 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','I','c','o','n','`',0};
5239 MSIQUERY *view;
5240 UINT r;
5242 r = MSI_DatabaseOpenViewW( package->db, query, &view );
5243 if (r == ERROR_SUCCESS)
5245 r = MSI_IterateRecords( view, NULL, ITERATE_UnpublishIcon, package );
5246 msiobj_release( &view->hdr );
5247 if (r != ERROR_SUCCESS)
5248 return r;
5250 return ERROR_SUCCESS;
5253 static UINT msi_unpublish_product( MSIPACKAGE *package, const WCHAR *remove )
5255 static const WCHAR szUpgradeCode[] = {'U','p','g','r','a','d','e','C','o','d','e',0};
5256 WCHAR *upgrade, **features;
5257 BOOL full_uninstall = TRUE;
5258 MSIFEATURE *feature;
5259 MSIPATCHINFO *patch;
5260 UINT i;
5262 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
5264 if (feature->Action == INSTALLSTATE_LOCAL) full_uninstall = FALSE;
5266 features = msi_split_string( remove, ',' );
5267 for (i = 0; features && features[i]; i++)
5269 if (!strcmpW( features[i], szAll )) full_uninstall = TRUE;
5271 msi_free(features);
5273 if (!full_uninstall)
5274 return ERROR_SUCCESS;
5276 MSIREG_DeleteProductKey(package->ProductCode);
5277 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5278 MSIREG_DeleteUninstallKey(package->ProductCode, package->platform);
5280 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
5281 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
5282 MSIREG_DeleteUserProductKey(package->ProductCode);
5283 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
5285 upgrade = msi_dup_property(package->db, szUpgradeCode);
5286 if (upgrade)
5288 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
5289 MSIREG_DeleteClassesUpgradeCodesKey(upgrade);
5290 msi_free(upgrade);
5293 LIST_FOR_EACH_ENTRY(patch, &package->patches, MSIPATCHINFO, entry)
5295 MSIREG_DeleteUserDataPatchKey(patch->patchcode, package->Context);
5296 if (!strcmpW( package->ProductCode, patch->products ))
5298 TRACE("removing local patch package %s\n", debugstr_w(patch->localfile));
5299 patch->delete_on_close = TRUE;
5301 /* FIXME: remove local patch package if this is the last product */
5303 TRACE("removing local package %s\n", debugstr_w(package->localfile));
5304 package->delete_on_close = TRUE;
5306 msi_unpublish_icons( package );
5307 return ERROR_SUCCESS;
5310 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
5312 UINT rc;
5313 WCHAR *remove;
5315 /* turn off scheduling */
5316 package->script->CurrentlyScripting= FALSE;
5318 /* first do the same as an InstallExecute */
5319 rc = ACTION_InstallExecute(package);
5320 if (rc != ERROR_SUCCESS)
5321 return rc;
5323 /* then handle commit actions */
5324 rc = execute_script(package, SCRIPT_COMMIT);
5325 if (rc != ERROR_SUCCESS)
5326 return rc;
5328 remove = msi_dup_property(package->db, szRemove);
5329 rc = msi_unpublish_product(package, remove);
5330 msi_free(remove);
5331 return rc;
5334 UINT ACTION_ForceReboot(MSIPACKAGE *package)
5336 static const WCHAR RunOnce[] = {
5337 'S','o','f','t','w','a','r','e','\\',
5338 'M','i','c','r','o','s','o','f','t','\\',
5339 'W','i','n','d','o','w','s','\\',
5340 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5341 'R','u','n','O','n','c','e',0};
5342 static const WCHAR InstallRunOnce[] = {
5343 'S','o','f','t','w','a','r','e','\\',
5344 'M','i','c','r','o','s','o','f','t','\\',
5345 'W','i','n','d','o','w','s','\\',
5346 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
5347 'I','n','s','t','a','l','l','e','r','\\',
5348 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
5350 static const WCHAR msiexec_fmt[] = {
5351 '%','s',
5352 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
5353 '\"','%','s','\"',0};
5354 static const WCHAR install_fmt[] = {
5355 '/','I',' ','\"','%','s','\"',' ',
5356 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
5357 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
5358 WCHAR buffer[256], sysdir[MAX_PATH];
5359 HKEY hkey;
5360 WCHAR squished_pc[100];
5362 squash_guid(package->ProductCode,squished_pc);
5364 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
5365 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
5366 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
5367 squished_pc);
5369 msi_reg_set_val_str( hkey, squished_pc, buffer );
5370 RegCloseKey(hkey);
5372 TRACE("Reboot command %s\n",debugstr_w(buffer));
5374 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
5375 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
5377 msi_reg_set_val_str( hkey, squished_pc, buffer );
5378 RegCloseKey(hkey);
5380 return ERROR_INSTALL_SUSPEND;
5383 WCHAR *msi_build_error_string( MSIPACKAGE *package, UINT error, DWORD count, ... )
5385 static const WCHAR query[] =
5386 {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
5387 'F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ',
5388 '`','E','r','r','o','r','`',' ','=',' ','%','i',0};
5389 MSIRECORD *rec, *row;
5390 DWORD i, size = 0;
5391 va_list va;
5392 const WCHAR *str;
5393 WCHAR *data;
5395 if (!(row = MSI_QueryGetRecord( package->db, query, error ))) return 0;
5397 rec = MSI_CreateRecord( count + 2 );
5398 str = MSI_RecordGetString( row, 1 );
5399 MSI_RecordSetStringW( rec, 0, str );
5400 msiobj_release( &row->hdr );
5401 MSI_RecordSetInteger( rec, 1, error );
5403 va_start( va, count );
5404 for (i = 0; i < count; i++)
5406 str = va_arg( va, const WCHAR *);
5407 MSI_RecordSetStringW( rec, i + 2, str );
5409 va_end( va );
5411 MSI_FormatRecordW( package, rec, NULL, &size );
5412 size++;
5413 data = msi_alloc( size * sizeof(WCHAR) );
5414 if (size > 1) MSI_FormatRecordW( package, rec, data, &size );
5415 else data[0] = 0;
5416 msiobj_release( &rec->hdr );
5417 return data;
5420 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
5422 DWORD attrib;
5423 UINT rc;
5426 * We are currently doing what should be done here in the top level Install
5427 * however for Administrative and uninstalls this step will be needed
5429 if (!package->PackagePath)
5430 return ERROR_SUCCESS;
5432 msi_set_sourcedir_props(package, TRUE);
5434 attrib = GetFileAttributesW(package->db->path);
5435 if (attrib == INVALID_FILE_ATTRIBUTES)
5437 LPWSTR prompt, msg;
5438 DWORD size = 0;
5440 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
5441 package->Context, MSICODE_PRODUCT,
5442 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
5443 if (rc == ERROR_MORE_DATA)
5445 prompt = msi_alloc(size * sizeof(WCHAR));
5446 MsiSourceListGetInfoW(package->ProductCode, NULL,
5447 package->Context, MSICODE_PRODUCT,
5448 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
5450 else
5451 prompt = strdupW(package->db->path);
5453 msg = msi_build_error_string(package, 1302, 1, prompt);
5454 msi_free(prompt);
5455 while(attrib == INVALID_FILE_ATTRIBUTES)
5457 rc = MessageBoxW(NULL, msg, NULL, MB_OKCANCEL);
5458 if (rc == IDCANCEL)
5460 msi_free(msg);
5461 return ERROR_INSTALL_USEREXIT;
5463 attrib = GetFileAttributesW(package->db->path);
5465 msi_free(msg);
5466 rc = ERROR_SUCCESS;
5468 else
5469 return ERROR_SUCCESS;
5471 return rc;
5474 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
5476 HKEY hkey = 0;
5477 LPWSTR buffer, productid = NULL;
5478 UINT i, rc = ERROR_SUCCESS;
5479 MSIRECORD *uirow;
5481 static const WCHAR szPropKeys[][80] =
5483 {'P','r','o','d','u','c','t','I','D',0},
5484 {'U','S','E','R','N','A','M','E',0},
5485 {'C','O','M','P','A','N','Y','N','A','M','E',0},
5486 {0},
5489 static const WCHAR szRegKeys[][80] =
5491 {'P','r','o','d','u','c','t','I','D',0},
5492 {'R','e','g','O','w','n','e','r',0},
5493 {'R','e','g','C','o','m','p','a','n','y',0},
5494 {0},
5497 if (msi_check_unpublish(package))
5499 MSIREG_DeleteUserDataProductKey(package->ProductCode);
5500 goto end;
5503 productid = msi_dup_property( package->db, INSTALLPROPERTY_PRODUCTIDW );
5504 if (!productid)
5505 goto end;
5507 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
5508 NULL, &hkey, TRUE);
5509 if (rc != ERROR_SUCCESS)
5510 goto end;
5512 for( i = 0; szPropKeys[i][0]; i++ )
5514 buffer = msi_dup_property( package->db, szPropKeys[i] );
5515 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
5516 msi_free( buffer );
5519 end:
5520 uirow = MSI_CreateRecord( 1 );
5521 MSI_RecordSetStringW( uirow, 1, productid );
5522 msi_ui_actiondata( package, szRegisterUser, uirow );
5523 msiobj_release( &uirow->hdr );
5525 msi_free(productid);
5526 RegCloseKey(hkey);
5527 return rc;
5531 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
5533 UINT rc;
5535 package->script->InWhatSequence |= SEQUENCE_EXEC;
5536 rc = ACTION_ProcessExecSequence(package,FALSE);
5537 return rc;
5540 WCHAR *msi_create_component_advertise_string( MSIPACKAGE *package, MSICOMPONENT *component, const WCHAR *feature )
5542 static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
5543 WCHAR productid_85[21], component_85[21], *ret;
5544 GUID clsid;
5545 DWORD sz;
5547 /* > is used if there is a component GUID and < if not. */
5549 productid_85[0] = 0;
5550 component_85[0] = 0;
5551 CLSIDFromString( package->ProductCode, &clsid );
5553 encode_base85_guid( &clsid, productid_85 );
5554 if (component)
5556 CLSIDFromString( component->ComponentId, &clsid );
5557 encode_base85_guid( &clsid, component_85 );
5560 TRACE("product=%s feature=%s component=%s\n", debugstr_w(productid_85), debugstr_w(feature),
5561 debugstr_w(component_85));
5563 sz = 20 + strlenW( feature ) + 20 + 3;
5564 ret = msi_alloc_zero( sz * sizeof(WCHAR) );
5565 if (ret) sprintfW( ret, fmt, productid_85, feature, component ? '>' : '<', component_85 );
5566 return ret;
5569 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
5571 MSIPACKAGE *package = param;
5572 LPCWSTR compgroupid, component, feature, qualifier, text;
5573 LPWSTR advertise = NULL, output = NULL, existing = NULL, p, q;
5574 HKEY hkey = NULL;
5575 UINT rc;
5576 MSICOMPONENT *comp;
5577 MSIFEATURE *feat;
5578 DWORD sz;
5579 MSIRECORD *uirow;
5580 int len;
5582 feature = MSI_RecordGetString(rec, 5);
5583 feat = msi_get_loaded_feature(package, feature);
5584 if (!feat)
5585 return ERROR_SUCCESS;
5587 feat->Action = msi_get_feature_action( package, feat );
5588 if (feat->Action != INSTALLSTATE_LOCAL &&
5589 feat->Action != INSTALLSTATE_SOURCE &&
5590 feat->Action != INSTALLSTATE_ADVERTISED)
5592 TRACE("feature not scheduled for installation %s\n", debugstr_w(feature));
5593 return ERROR_SUCCESS;
5596 component = MSI_RecordGetString(rec, 3);
5597 comp = msi_get_loaded_component(package, component);
5598 if (!comp)
5599 return ERROR_SUCCESS;
5601 compgroupid = MSI_RecordGetString(rec,1);
5602 qualifier = MSI_RecordGetString(rec,2);
5604 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
5605 if (rc != ERROR_SUCCESS)
5606 goto end;
5608 advertise = msi_create_component_advertise_string( package, comp, feature );
5609 text = MSI_RecordGetString( rec, 4 );
5610 if (text)
5612 p = msi_alloc( (strlenW( advertise ) + strlenW( text ) + 1) * sizeof(WCHAR) );
5613 strcpyW( p, advertise );
5614 strcatW( p, text );
5615 msi_free( advertise );
5616 advertise = p;
5618 existing = msi_reg_get_val_str( hkey, qualifier );
5620 sz = strlenW( advertise ) + 1;
5621 if (existing)
5623 for (p = existing; *p; p += len)
5625 len = strlenW( p ) + 1;
5626 if (strcmpW( advertise, p )) sz += len;
5629 if (!(output = msi_alloc( (sz + 1) * sizeof(WCHAR) )))
5631 rc = ERROR_OUTOFMEMORY;
5632 goto end;
5634 q = output;
5635 if (existing)
5637 for (p = existing; *p; p += len)
5639 len = strlenW( p ) + 1;
5640 if (strcmpW( advertise, p ))
5642 memcpy( q, p, len * sizeof(WCHAR) );
5643 q += len;
5647 strcpyW( q, advertise );
5648 q[strlenW( q ) + 1] = 0;
5650 msi_reg_set_val_multi_str( hkey, qualifier, output );
5652 end:
5653 RegCloseKey(hkey);
5654 msi_free( output );
5655 msi_free( advertise );
5656 msi_free( existing );
5658 /* the UI chunk */
5659 uirow = MSI_CreateRecord( 2 );
5660 MSI_RecordSetStringW( uirow, 1, compgroupid );
5661 MSI_RecordSetStringW( uirow, 2, qualifier);
5662 msi_ui_actiondata( package, szPublishComponents, uirow );
5663 msiobj_release( &uirow->hdr );
5664 /* FIXME: call ui_progress? */
5666 return rc;
5670 * At present I am ignorning the advertised components part of this and only
5671 * focusing on the qualified component sets
5673 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
5675 static const WCHAR query[] = {
5676 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5677 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5678 MSIQUERY *view;
5679 UINT rc;
5681 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5682 if (rc != ERROR_SUCCESS)
5683 return ERROR_SUCCESS;
5685 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
5686 msiobj_release(&view->hdr);
5687 return rc;
5690 static UINT ITERATE_UnpublishComponent( MSIRECORD *rec, LPVOID param )
5692 static const WCHAR szInstallerComponents[] = {
5693 'S','o','f','t','w','a','r','e','\\',
5694 'M','i','c','r','o','s','o','f','t','\\',
5695 'I','n','s','t','a','l','l','e','r','\\',
5696 'C','o','m','p','o','n','e','n','t','s','\\',0};
5698 MSIPACKAGE *package = param;
5699 LPCWSTR compgroupid, component, feature, qualifier;
5700 MSICOMPONENT *comp;
5701 MSIFEATURE *feat;
5702 MSIRECORD *uirow;
5703 WCHAR squashed[GUID_SIZE], keypath[MAX_PATH];
5704 LONG res;
5706 feature = MSI_RecordGetString( rec, 5 );
5707 feat = msi_get_loaded_feature( package, feature );
5708 if (!feat)
5709 return ERROR_SUCCESS;
5711 feat->Action = msi_get_feature_action( package, feat );
5712 if (feat->Action != INSTALLSTATE_ABSENT)
5714 TRACE("feature not scheduled for removal %s\n", debugstr_w(feature));
5715 return ERROR_SUCCESS;
5718 component = MSI_RecordGetString( rec, 3 );
5719 comp = msi_get_loaded_component( package, component );
5720 if (!comp)
5721 return ERROR_SUCCESS;
5723 compgroupid = MSI_RecordGetString( rec, 1 );
5724 qualifier = MSI_RecordGetString( rec, 2 );
5726 squash_guid( compgroupid, squashed );
5727 strcpyW( keypath, szInstallerComponents );
5728 strcatW( keypath, squashed );
5730 res = RegDeleteKeyW( HKEY_CURRENT_USER, keypath );
5731 if (res != ERROR_SUCCESS)
5733 WARN("Unable to delete component key %d\n", res);
5736 uirow = MSI_CreateRecord( 2 );
5737 MSI_RecordSetStringW( uirow, 1, compgroupid );
5738 MSI_RecordSetStringW( uirow, 2, qualifier );
5739 msi_ui_actiondata( package, szUnpublishComponents, uirow );
5740 msiobj_release( &uirow->hdr );
5742 return ERROR_SUCCESS;
5745 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
5747 static const WCHAR query[] = {
5748 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5749 '`','P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','`',0};
5750 MSIQUERY *view;
5751 UINT rc;
5753 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
5754 if (rc != ERROR_SUCCESS)
5755 return ERROR_SUCCESS;
5757 rc = MSI_IterateRecords( view, NULL, ITERATE_UnpublishComponent, package );
5758 msiobj_release( &view->hdr );
5759 return rc;
5762 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
5764 static const WCHAR query[] =
5765 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5766 '`','C','o','m','p','o','n','e','n','t','`',' ','W','H','E','R','E',' ',
5767 '`','C','o','m','p','o','n','e','n','t','`',' ','=','\'','%','s','\'',0};
5768 MSIPACKAGE *package = param;
5769 MSICOMPONENT *component;
5770 MSIRECORD *row;
5771 MSIFILE *file;
5772 SC_HANDLE hscm = NULL, service = NULL;
5773 LPCWSTR comp, key;
5774 LPWSTR name = NULL, disp = NULL, load_order = NULL, serv_name = NULL;
5775 LPWSTR depends = NULL, pass = NULL, args = NULL, image_path = NULL;
5776 DWORD serv_type, start_type, err_control;
5777 SERVICE_DESCRIPTIONW sd = {NULL};
5778 UINT ret = ERROR_SUCCESS;
5780 comp = MSI_RecordGetString( rec, 12 );
5781 component = msi_get_loaded_component( package, comp );
5782 if (!component)
5784 WARN("service component not found\n");
5785 goto done;
5787 component->Action = msi_get_component_action( package, component );
5788 if (component->Action != INSTALLSTATE_LOCAL)
5790 TRACE("component not scheduled for installation %s\n", debugstr_w(comp));
5791 goto done;
5793 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
5794 if (!hscm)
5796 ERR("Failed to open the SC Manager!\n");
5797 goto done;
5800 start_type = MSI_RecordGetInteger(rec, 5);
5801 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
5802 goto done;
5804 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5805 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
5806 serv_type = MSI_RecordGetInteger(rec, 4);
5807 err_control = MSI_RecordGetInteger(rec, 6);
5808 deformat_string(package, MSI_RecordGetString(rec, 7), &load_order);
5809 deformat_string(package, MSI_RecordGetString(rec, 8), &depends);
5810 deformat_string(package, MSI_RecordGetString(rec, 9), &serv_name);
5811 deformat_string(package, MSI_RecordGetString(rec, 10), &pass);
5812 deformat_string(package, MSI_RecordGetString(rec, 11), &args);
5813 deformat_string(package, MSI_RecordGetString(rec, 13), &sd.lpDescription);
5815 /* fetch the service path */
5816 row = MSI_QueryGetRecord(package->db, query, comp);
5817 if (!row)
5819 ERR("Query failed\n");
5820 goto done;
5822 key = MSI_RecordGetString(row, 6);
5823 file = msi_get_loaded_file(package, key);
5824 msiobj_release(&row->hdr);
5825 if (!file)
5827 ERR("Failed to load the service file\n");
5828 goto done;
5831 if (!args || !args[0]) image_path = file->TargetPath;
5832 else
5834 int len = strlenW(file->TargetPath) + strlenW(args) + 2;
5835 if (!(image_path = msi_alloc(len * sizeof(WCHAR))))
5837 ret = ERROR_OUTOFMEMORY;
5838 goto done;
5841 strcpyW(image_path, file->TargetPath);
5842 strcatW(image_path, szSpace);
5843 strcatW(image_path, args);
5845 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
5846 start_type, err_control, image_path, load_order,
5847 NULL, depends, serv_name, pass);
5849 if (!service)
5851 if (GetLastError() != ERROR_SERVICE_EXISTS)
5852 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
5854 else if (sd.lpDescription)
5856 if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &sd))
5857 WARN("failed to set service description %u\n", GetLastError());
5860 if (image_path != file->TargetPath) msi_free(image_path);
5861 done:
5862 CloseServiceHandle(service);
5863 CloseServiceHandle(hscm);
5864 msi_free(name);
5865 msi_free(disp);
5866 msi_free(sd.lpDescription);
5867 msi_free(load_order);
5868 msi_free(serv_name);
5869 msi_free(pass);
5870 msi_free(depends);
5871 msi_free(args);
5873 return ret;
5876 static UINT ACTION_InstallServices( MSIPACKAGE *package )
5878 static const WCHAR query[] = {
5879 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5880 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
5881 MSIQUERY *view;
5882 UINT rc;
5884 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5885 if (rc != ERROR_SUCCESS)
5886 return ERROR_SUCCESS;
5888 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
5889 msiobj_release(&view->hdr);
5890 return rc;
5893 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
5894 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
5896 LPCWSTR *vector, *temp_vector;
5897 LPWSTR p, q;
5898 DWORD sep_len;
5900 static const WCHAR separator[] = {'[','~',']',0};
5902 *numargs = 0;
5903 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
5905 if (!args)
5906 return NULL;
5908 vector = msi_alloc(sizeof(LPWSTR));
5909 if (!vector)
5910 return NULL;
5912 p = args;
5915 (*numargs)++;
5916 vector[*numargs - 1] = p;
5918 if ((q = strstrW(p, separator)))
5920 *q = '\0';
5922 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
5923 if (!temp_vector)
5925 msi_free(vector);
5926 return NULL;
5928 vector = temp_vector;
5930 p = q + sep_len;
5932 } while (q);
5934 return vector;
5937 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
5939 MSIPACKAGE *package = param;
5940 MSICOMPONENT *comp;
5941 MSIRECORD *uirow;
5942 SC_HANDLE scm = NULL, service = NULL;
5943 LPCWSTR component, *vector = NULL;
5944 LPWSTR name, args, display_name = NULL;
5945 DWORD event, numargs, len, wait, dummy;
5946 UINT r = ERROR_FUNCTION_FAILED;
5947 SERVICE_STATUS_PROCESS status;
5948 ULONGLONG start_time;
5950 component = MSI_RecordGetString(rec, 6);
5951 comp = msi_get_loaded_component(package, component);
5952 if (!comp)
5953 return ERROR_SUCCESS;
5955 comp->Action = msi_get_component_action( package, comp );
5956 if (comp->Action != INSTALLSTATE_LOCAL)
5958 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
5959 return ERROR_SUCCESS;
5962 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
5963 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
5964 event = MSI_RecordGetInteger(rec, 3);
5965 wait = MSI_RecordGetInteger(rec, 5);
5967 if (!(event & msidbServiceControlEventStart))
5969 r = ERROR_SUCCESS;
5970 goto done;
5973 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
5974 if (!scm)
5976 ERR("Failed to open the service control manager\n");
5977 goto done;
5980 len = 0;
5981 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
5982 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5984 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
5985 GetServiceDisplayNameW( scm, name, display_name, &len );
5988 service = OpenServiceW(scm, name, SERVICE_START|SERVICE_QUERY_STATUS);
5989 if (!service)
5991 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
5992 goto done;
5995 vector = msi_service_args_to_vector(args, &numargs);
5997 if (!StartServiceW(service, numargs, vector) &&
5998 GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
6000 ERR("Failed to start service %s (%u)\n", debugstr_w(name), GetLastError());
6001 goto done;
6004 r = ERROR_SUCCESS;
6005 if (wait)
6007 /* wait for at most 30 seconds for the service to be up and running */
6008 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6009 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6011 TRACE("failed to query service status (%u)\n", GetLastError());
6012 goto done;
6014 start_time = GetTickCount64();
6015 while (status.dwCurrentState == SERVICE_START_PENDING)
6017 if (GetTickCount64() - start_time > 30000) break;
6018 Sleep(1000);
6019 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
6020 (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy))
6022 TRACE("failed to query service status (%u)\n", GetLastError());
6023 goto done;
6026 if (status.dwCurrentState != SERVICE_RUNNING)
6028 WARN("service failed to start %u\n", status.dwCurrentState);
6029 r = ERROR_FUNCTION_FAILED;
6033 done:
6034 uirow = MSI_CreateRecord( 2 );
6035 MSI_RecordSetStringW( uirow, 1, display_name );
6036 MSI_RecordSetStringW( uirow, 2, name );
6037 msi_ui_actiondata( package, szStartServices, uirow );
6038 msiobj_release( &uirow->hdr );
6040 CloseServiceHandle(service);
6041 CloseServiceHandle(scm);
6043 msi_free(name);
6044 msi_free(args);
6045 msi_free(vector);
6046 msi_free(display_name);
6047 return r;
6050 static UINT ACTION_StartServices( MSIPACKAGE *package )
6052 static const WCHAR query[] = {
6053 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6054 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6055 MSIQUERY *view;
6056 UINT rc;
6058 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6059 if (rc != ERROR_SUCCESS)
6060 return ERROR_SUCCESS;
6062 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
6063 msiobj_release(&view->hdr);
6064 return rc;
6067 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
6069 DWORD i, needed, count;
6070 ENUM_SERVICE_STATUSW *dependencies;
6071 SERVICE_STATUS ss;
6072 SC_HANDLE depserv;
6073 BOOL stopped, ret = FALSE;
6075 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
6076 0, &needed, &count))
6077 return TRUE;
6079 if (GetLastError() != ERROR_MORE_DATA)
6080 return FALSE;
6082 dependencies = msi_alloc(needed);
6083 if (!dependencies)
6084 return FALSE;
6086 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
6087 needed, &needed, &count))
6088 goto done;
6090 for (i = 0; i < count; i++)
6092 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
6093 SERVICE_STOP | SERVICE_QUERY_STATUS);
6094 if (!depserv)
6095 goto done;
6097 stopped = ControlService(depserv, SERVICE_CONTROL_STOP, &ss);
6098 CloseServiceHandle(depserv);
6099 if (!stopped)
6100 goto done;
6103 ret = TRUE;
6105 done:
6106 msi_free(dependencies);
6107 return ret;
6110 static UINT stop_service( LPCWSTR name )
6112 SC_HANDLE scm = NULL, service = NULL;
6113 SERVICE_STATUS status;
6114 SERVICE_STATUS_PROCESS ssp;
6115 DWORD needed;
6117 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
6118 if (!scm)
6120 WARN("Failed to open the SCM: %d\n", GetLastError());
6121 goto done;
6124 service = OpenServiceW(scm, name,
6125 SERVICE_STOP |
6126 SERVICE_QUERY_STATUS |
6127 SERVICE_ENUMERATE_DEPENDENTS);
6128 if (!service)
6130 WARN("Failed to open service (%s): %d\n", debugstr_w(name), GetLastError());
6131 goto done;
6134 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
6135 sizeof(SERVICE_STATUS_PROCESS), &needed))
6137 WARN("Failed to query service status (%s): %d\n", debugstr_w(name), GetLastError());
6138 goto done;
6141 if (ssp.dwCurrentState == SERVICE_STOPPED)
6142 goto done;
6144 stop_service_dependents(scm, service);
6146 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
6147 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
6149 done:
6150 CloseServiceHandle(service);
6151 CloseServiceHandle(scm);
6153 return ERROR_SUCCESS;
6156 static UINT ITERATE_StopService( MSIRECORD *rec, LPVOID param )
6158 MSIPACKAGE *package = param;
6159 MSICOMPONENT *comp;
6160 MSIRECORD *uirow;
6161 LPCWSTR component;
6162 LPWSTR name = NULL, display_name = NULL;
6163 DWORD event, len;
6164 SC_HANDLE scm;
6166 event = MSI_RecordGetInteger( rec, 3 );
6167 if (!(event & msidbServiceControlEventStop))
6168 return ERROR_SUCCESS;
6170 component = MSI_RecordGetString( rec, 6 );
6171 comp = msi_get_loaded_component( package, component );
6172 if (!comp)
6173 return ERROR_SUCCESS;
6175 comp->Action = msi_get_component_action( package, comp );
6176 if (comp->Action != INSTALLSTATE_ABSENT)
6178 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6179 return ERROR_SUCCESS;
6182 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
6183 if (!scm)
6185 ERR("Failed to open the service control manager\n");
6186 goto done;
6189 len = 0;
6190 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6191 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6193 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6194 GetServiceDisplayNameW( scm, name, display_name, &len );
6196 CloseServiceHandle( scm );
6198 deformat_string( package, MSI_RecordGetString( rec, 2 ), &name );
6199 stop_service( name );
6201 done:
6202 uirow = MSI_CreateRecord( 2 );
6203 MSI_RecordSetStringW( uirow, 1, display_name );
6204 MSI_RecordSetStringW( uirow, 2, name );
6205 msi_ui_actiondata( package, szStopServices, uirow );
6206 msiobj_release( &uirow->hdr );
6208 msi_free( name );
6209 msi_free( display_name );
6210 return ERROR_SUCCESS;
6213 static UINT ACTION_StopServices( MSIPACKAGE *package )
6215 static const WCHAR query[] = {
6216 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6217 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6218 MSIQUERY *view;
6219 UINT rc;
6221 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
6222 if (rc != ERROR_SUCCESS)
6223 return ERROR_SUCCESS;
6225 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
6226 msiobj_release(&view->hdr);
6227 return rc;
6230 static UINT ITERATE_DeleteService( MSIRECORD *rec, LPVOID param )
6232 MSIPACKAGE *package = param;
6233 MSICOMPONENT *comp;
6234 MSIRECORD *uirow;
6235 LPWSTR name = NULL, display_name = NULL;
6236 DWORD event, len;
6237 SC_HANDLE scm = NULL, service = NULL;
6239 comp = msi_get_loaded_component( package, MSI_RecordGetString(rec, 6) );
6240 if (!comp)
6241 return ERROR_SUCCESS;
6243 event = MSI_RecordGetInteger( rec, 3 );
6244 deformat_string( package, MSI_RecordGetString(rec, 2), &name );
6246 comp->Action = msi_get_component_action( package, comp );
6247 if (!(comp->Action == INSTALLSTATE_LOCAL && (event & msidbServiceControlEventDelete)) &&
6248 !(comp->Action == INSTALLSTATE_ABSENT && (event & msidbServiceControlEventUninstallDelete)))
6250 TRACE("service %s not scheduled for removal\n", debugstr_w(name));
6251 msi_free( name );
6252 return ERROR_SUCCESS;
6254 stop_service( name );
6256 scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
6257 if (!scm)
6259 WARN("Failed to open the SCM: %d\n", GetLastError());
6260 goto done;
6263 len = 0;
6264 if (!GetServiceDisplayNameW( scm, name, NULL, &len ) &&
6265 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
6267 if ((display_name = msi_alloc( ++len * sizeof(WCHAR ))))
6268 GetServiceDisplayNameW( scm, name, display_name, &len );
6271 service = OpenServiceW( scm, name, DELETE );
6272 if (!service)
6274 WARN("Failed to open service (%s): %u\n", debugstr_w(name), GetLastError());
6275 goto done;
6278 if (!DeleteService( service ))
6279 WARN("Failed to delete service (%s): %u\n", debugstr_w(name), GetLastError());
6281 done:
6282 uirow = MSI_CreateRecord( 2 );
6283 MSI_RecordSetStringW( uirow, 1, display_name );
6284 MSI_RecordSetStringW( uirow, 2, name );
6285 msi_ui_actiondata( package, szDeleteServices, uirow );
6286 msiobj_release( &uirow->hdr );
6288 CloseServiceHandle( service );
6289 CloseServiceHandle( scm );
6290 msi_free( name );
6291 msi_free( display_name );
6293 return ERROR_SUCCESS;
6296 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6298 static const WCHAR query[] = {
6299 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6300 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0};
6301 MSIQUERY *view;
6302 UINT rc;
6304 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
6305 if (rc != ERROR_SUCCESS)
6306 return ERROR_SUCCESS;
6308 rc = MSI_IterateRecords( view, NULL, ITERATE_DeleteService, package );
6309 msiobj_release( &view->hdr );
6310 return rc;
6313 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
6315 MSIPACKAGE *package = param;
6316 LPWSTR driver, driver_path, ptr;
6317 WCHAR outpath[MAX_PATH];
6318 MSIFILE *driver_file = NULL, *setup_file = NULL;
6319 MSICOMPONENT *comp;
6320 MSIRECORD *uirow;
6321 LPCWSTR desc, file_key, component;
6322 DWORD len, usage;
6323 UINT r = ERROR_SUCCESS;
6325 static const WCHAR driver_fmt[] = {
6326 'D','r','i','v','e','r','=','%','s',0};
6327 static const WCHAR setup_fmt[] = {
6328 'S','e','t','u','p','=','%','s',0};
6329 static const WCHAR usage_fmt[] = {
6330 'F','i','l','e','U','s','a','g','e','=','1',0};
6332 component = MSI_RecordGetString( rec, 2 );
6333 comp = msi_get_loaded_component( package, component );
6334 if (!comp)
6335 return ERROR_SUCCESS;
6337 comp->Action = msi_get_component_action( package, comp );
6338 if (comp->Action != INSTALLSTATE_LOCAL)
6340 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6341 return ERROR_SUCCESS;
6343 desc = MSI_RecordGetString(rec, 3);
6345 file_key = MSI_RecordGetString( rec, 4 );
6346 if (file_key) driver_file = msi_get_loaded_file( package, file_key );
6348 file_key = MSI_RecordGetString( rec, 5 );
6349 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6351 if (!driver_file)
6353 ERR("ODBC Driver entry not found!\n");
6354 return ERROR_FUNCTION_FAILED;
6357 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName);
6358 if (setup_file)
6359 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6360 len += lstrlenW(usage_fmt) + 2; /* \0\0 */
6362 driver = msi_alloc(len * sizeof(WCHAR));
6363 if (!driver)
6364 return ERROR_OUTOFMEMORY;
6366 ptr = driver;
6367 lstrcpyW(ptr, desc);
6368 ptr += lstrlenW(ptr) + 1;
6370 len = sprintfW(ptr, driver_fmt, driver_file->FileName);
6371 ptr += len + 1;
6373 if (setup_file)
6375 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6376 ptr += len + 1;
6379 lstrcpyW(ptr, usage_fmt);
6380 ptr += lstrlenW(ptr) + 1;
6381 *ptr = '\0';
6383 if (!driver_file->TargetPath)
6385 const WCHAR *dir = msi_get_target_folder( package, driver_file->Component->Directory );
6386 driver_file->TargetPath = msi_build_directory_name( 2, dir, driver_file->FileName );
6388 driver_path = strdupW(driver_file->TargetPath);
6389 ptr = strrchrW(driver_path, '\\');
6390 if (ptr) *ptr = '\0';
6392 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
6393 NULL, ODBC_INSTALL_COMPLETE, &usage))
6395 ERR("Failed to install SQL driver!\n");
6396 r = ERROR_FUNCTION_FAILED;
6399 uirow = MSI_CreateRecord( 5 );
6400 MSI_RecordSetStringW( uirow, 1, desc );
6401 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6402 MSI_RecordSetStringW( uirow, 3, driver_file->Component->Directory );
6403 msi_ui_actiondata( package, szInstallODBC, uirow );
6404 msiobj_release( &uirow->hdr );
6406 msi_free(driver);
6407 msi_free(driver_path);
6409 return r;
6412 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
6414 MSIPACKAGE *package = param;
6415 LPWSTR translator, translator_path, ptr;
6416 WCHAR outpath[MAX_PATH];
6417 MSIFILE *translator_file = NULL, *setup_file = NULL;
6418 MSICOMPONENT *comp;
6419 MSIRECORD *uirow;
6420 LPCWSTR desc, file_key, component;
6421 DWORD len, usage;
6422 UINT r = ERROR_SUCCESS;
6424 static const WCHAR translator_fmt[] = {
6425 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
6426 static const WCHAR setup_fmt[] = {
6427 'S','e','t','u','p','=','%','s',0};
6429 component = MSI_RecordGetString( rec, 2 );
6430 comp = msi_get_loaded_component( package, component );
6431 if (!comp)
6432 return ERROR_SUCCESS;
6434 comp->Action = msi_get_component_action( package, comp );
6435 if (comp->Action != INSTALLSTATE_LOCAL)
6437 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6438 return ERROR_SUCCESS;
6440 desc = MSI_RecordGetString(rec, 3);
6442 file_key = MSI_RecordGetString( rec, 4 );
6443 if (file_key) translator_file = msi_get_loaded_file( package, file_key );
6445 file_key = MSI_RecordGetString( rec, 5 );
6446 if (file_key) setup_file = msi_get_loaded_file( package, file_key );
6448 if (!translator_file)
6450 ERR("ODBC Translator entry not found!\n");
6451 return ERROR_FUNCTION_FAILED;
6454 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) + 2; /* \0\0 */
6455 if (setup_file)
6456 len += lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName);
6458 translator = msi_alloc(len * sizeof(WCHAR));
6459 if (!translator)
6460 return ERROR_OUTOFMEMORY;
6462 ptr = translator;
6463 lstrcpyW(ptr, desc);
6464 ptr += lstrlenW(ptr) + 1;
6466 len = sprintfW(ptr, translator_fmt, translator_file->FileName);
6467 ptr += len + 1;
6469 if (setup_file)
6471 len = sprintfW(ptr, setup_fmt, setup_file->FileName);
6472 ptr += len + 1;
6474 *ptr = '\0';
6476 translator_path = strdupW(translator_file->TargetPath);
6477 ptr = strrchrW(translator_path, '\\');
6478 if (ptr) *ptr = '\0';
6480 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
6481 NULL, ODBC_INSTALL_COMPLETE, &usage))
6483 ERR("Failed to install SQL translator!\n");
6484 r = ERROR_FUNCTION_FAILED;
6487 uirow = MSI_CreateRecord( 5 );
6488 MSI_RecordSetStringW( uirow, 1, desc );
6489 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6490 MSI_RecordSetStringW( uirow, 3, translator_file->Component->Directory );
6491 msi_ui_actiondata( package, szInstallODBC, uirow );
6492 msiobj_release( &uirow->hdr );
6494 msi_free(translator);
6495 msi_free(translator_path);
6497 return r;
6500 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
6502 MSIPACKAGE *package = param;
6503 MSICOMPONENT *comp;
6504 LPWSTR attrs;
6505 LPCWSTR desc, driver, component;
6506 WORD request = ODBC_ADD_SYS_DSN;
6507 INT registration;
6508 DWORD len;
6509 UINT r = ERROR_SUCCESS;
6510 MSIRECORD *uirow;
6512 static const WCHAR attrs_fmt[] = {
6513 'D','S','N','=','%','s',0 };
6515 component = MSI_RecordGetString( rec, 2 );
6516 comp = msi_get_loaded_component( package, component );
6517 if (!comp)
6518 return ERROR_SUCCESS;
6520 comp->Action = msi_get_component_action( package, comp );
6521 if (comp->Action != INSTALLSTATE_LOCAL)
6523 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6524 return ERROR_SUCCESS;
6527 desc = MSI_RecordGetString(rec, 3);
6528 driver = MSI_RecordGetString(rec, 4);
6529 registration = MSI_RecordGetInteger(rec, 5);
6531 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
6532 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
6534 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 2; /* \0\0 */
6535 attrs = msi_alloc(len * sizeof(WCHAR));
6536 if (!attrs)
6537 return ERROR_OUTOFMEMORY;
6539 len = sprintfW(attrs, attrs_fmt, desc);
6540 attrs[len + 1] = 0;
6542 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
6544 ERR("Failed to install SQL data source!\n");
6545 r = ERROR_FUNCTION_FAILED;
6548 uirow = MSI_CreateRecord( 5 );
6549 MSI_RecordSetStringW( uirow, 1, desc );
6550 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6551 MSI_RecordSetInteger( uirow, 3, request );
6552 msi_ui_actiondata( package, szInstallODBC, uirow );
6553 msiobj_release( &uirow->hdr );
6555 msi_free(attrs);
6557 return r;
6560 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
6562 static const WCHAR driver_query[] = {
6563 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6564 'O','D','B','C','D','r','i','v','e','r',0};
6565 static const WCHAR translator_query[] = {
6566 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6567 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6568 static const WCHAR source_query[] = {
6569 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6570 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6571 MSIQUERY *view;
6572 UINT rc;
6574 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
6575 if (rc == ERROR_SUCCESS)
6577 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
6578 msiobj_release(&view->hdr);
6579 if (rc != ERROR_SUCCESS)
6580 return rc;
6582 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
6583 if (rc == ERROR_SUCCESS)
6585 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
6586 msiobj_release(&view->hdr);
6587 if (rc != ERROR_SUCCESS)
6588 return rc;
6590 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
6591 if (rc == ERROR_SUCCESS)
6593 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
6594 msiobj_release(&view->hdr);
6595 if (rc != ERROR_SUCCESS)
6596 return rc;
6598 return ERROR_SUCCESS;
6601 static UINT ITERATE_RemoveODBCDriver( MSIRECORD *rec, LPVOID param )
6603 MSIPACKAGE *package = param;
6604 MSICOMPONENT *comp;
6605 MSIRECORD *uirow;
6606 DWORD usage;
6607 LPCWSTR desc, component;
6609 component = MSI_RecordGetString( rec, 2 );
6610 comp = msi_get_loaded_component( package, component );
6611 if (!comp)
6612 return ERROR_SUCCESS;
6614 comp->Action = msi_get_component_action( package, comp );
6615 if (comp->Action != INSTALLSTATE_ABSENT)
6617 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6618 return ERROR_SUCCESS;
6621 desc = MSI_RecordGetString( rec, 3 );
6622 if (!SQLRemoveDriverW( desc, FALSE, &usage ))
6624 WARN("Failed to remove ODBC driver\n");
6626 else if (!usage)
6628 FIXME("Usage count reached 0\n");
6631 uirow = MSI_CreateRecord( 2 );
6632 MSI_RecordSetStringW( uirow, 1, desc );
6633 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6634 msi_ui_actiondata( package, szRemoveODBC, uirow );
6635 msiobj_release( &uirow->hdr );
6637 return ERROR_SUCCESS;
6640 static UINT ITERATE_RemoveODBCTranslator( MSIRECORD *rec, LPVOID param )
6642 MSIPACKAGE *package = param;
6643 MSICOMPONENT *comp;
6644 MSIRECORD *uirow;
6645 DWORD usage;
6646 LPCWSTR desc, component;
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_ABSENT)
6656 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6657 return ERROR_SUCCESS;
6660 desc = MSI_RecordGetString( rec, 3 );
6661 if (!SQLRemoveTranslatorW( desc, &usage ))
6663 WARN("Failed to remove ODBC translator\n");
6665 else if (!usage)
6667 FIXME("Usage count reached 0\n");
6670 uirow = MSI_CreateRecord( 2 );
6671 MSI_RecordSetStringW( uirow, 1, desc );
6672 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6673 msi_ui_actiondata( package, szRemoveODBC, uirow );
6674 msiobj_release( &uirow->hdr );
6676 return ERROR_SUCCESS;
6679 static UINT ITERATE_RemoveODBCDataSource( MSIRECORD *rec, LPVOID param )
6681 MSIPACKAGE *package = param;
6682 MSICOMPONENT *comp;
6683 MSIRECORD *uirow;
6684 LPWSTR attrs;
6685 LPCWSTR desc, driver, component;
6686 WORD request = ODBC_REMOVE_SYS_DSN;
6687 INT registration;
6688 DWORD len;
6690 static const WCHAR attrs_fmt[] = {
6691 'D','S','N','=','%','s',0 };
6693 component = MSI_RecordGetString( rec, 2 );
6694 comp = msi_get_loaded_component( package, component );
6695 if (!comp)
6696 return ERROR_SUCCESS;
6698 comp->Action = msi_get_component_action( package, comp );
6699 if (comp->Action != INSTALLSTATE_ABSENT)
6701 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
6702 return ERROR_SUCCESS;
6705 desc = MSI_RecordGetString( rec, 3 );
6706 driver = MSI_RecordGetString( rec, 4 );
6707 registration = MSI_RecordGetInteger( rec, 5 );
6709 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_REMOVE_SYS_DSN;
6710 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_REMOVE_DSN;
6712 len = strlenW( attrs_fmt ) + strlenW( desc ) + 2; /* \0\0 */
6713 attrs = msi_alloc( len * sizeof(WCHAR) );
6714 if (!attrs)
6715 return ERROR_OUTOFMEMORY;
6717 FIXME("Use ODBCSourceAttribute table\n");
6719 len = sprintfW( attrs, attrs_fmt, desc );
6720 attrs[len + 1] = 0;
6722 if (!SQLConfigDataSourceW( NULL, request, driver, attrs ))
6724 WARN("Failed to remove ODBC data source\n");
6726 msi_free( attrs );
6728 uirow = MSI_CreateRecord( 3 );
6729 MSI_RecordSetStringW( uirow, 1, desc );
6730 MSI_RecordSetStringW( uirow, 2, MSI_RecordGetString(rec, 2) );
6731 MSI_RecordSetInteger( uirow, 3, request );
6732 msi_ui_actiondata( package, szRemoveODBC, uirow );
6733 msiobj_release( &uirow->hdr );
6735 return ERROR_SUCCESS;
6738 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6740 static const WCHAR driver_query[] = {
6741 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6742 'O','D','B','C','D','r','i','v','e','r',0};
6743 static const WCHAR translator_query[] = {
6744 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6745 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
6746 static const WCHAR source_query[] = {
6747 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6748 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0};
6749 MSIQUERY *view;
6750 UINT rc;
6752 rc = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
6753 if (rc == ERROR_SUCCESS)
6755 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDriver, package );
6756 msiobj_release( &view->hdr );
6757 if (rc != ERROR_SUCCESS)
6758 return rc;
6760 rc = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
6761 if (rc == ERROR_SUCCESS)
6763 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCTranslator, package );
6764 msiobj_release( &view->hdr );
6765 if (rc != ERROR_SUCCESS)
6766 return rc;
6768 rc = MSI_DatabaseOpenViewW( package->db, source_query, &view );
6769 if (rc == ERROR_SUCCESS)
6771 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveODBCDataSource, package );
6772 msiobj_release( &view->hdr );
6773 if (rc != ERROR_SUCCESS)
6774 return rc;
6776 return ERROR_SUCCESS;
6779 #define ENV_ACT_SETALWAYS 0x1
6780 #define ENV_ACT_SETABSENT 0x2
6781 #define ENV_ACT_REMOVE 0x4
6782 #define ENV_ACT_REMOVEMATCH 0x8
6784 #define ENV_MOD_MACHINE 0x20000000
6785 #define ENV_MOD_APPEND 0x40000000
6786 #define ENV_MOD_PREFIX 0x80000000
6787 #define ENV_MOD_MASK 0xC0000000
6789 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
6791 static UINT env_parse_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
6793 LPCWSTR cptr = *name;
6795 static const WCHAR prefix[] = {'[','~',']',0};
6796 static const int prefix_len = 3;
6798 *flags = 0;
6799 while (*cptr)
6801 if (*cptr == '=')
6802 *flags |= ENV_ACT_SETALWAYS;
6803 else if (*cptr == '+')
6804 *flags |= ENV_ACT_SETABSENT;
6805 else if (*cptr == '-')
6806 *flags |= ENV_ACT_REMOVE;
6807 else if (*cptr == '!')
6808 *flags |= ENV_ACT_REMOVEMATCH;
6809 else if (*cptr == '*')
6810 *flags |= ENV_MOD_MACHINE;
6811 else
6812 break;
6814 cptr++;
6815 (*name)++;
6818 if (!*cptr)
6820 ERR("Missing environment variable\n");
6821 return ERROR_FUNCTION_FAILED;
6824 if (*value)
6826 LPCWSTR ptr = *value;
6827 if (!strncmpW(ptr, prefix, prefix_len))
6829 if (ptr[prefix_len] == szSemiColon[0])
6831 *flags |= ENV_MOD_APPEND;
6832 *value += lstrlenW(prefix);
6834 else
6836 *value = NULL;
6839 else if (lstrlenW(*value) >= prefix_len)
6841 ptr += lstrlenW(ptr) - prefix_len;
6842 if (!strcmpW( ptr, prefix ))
6844 if ((ptr-1) > *value && *(ptr-1) == szSemiColon[0])
6846 *flags |= ENV_MOD_PREFIX;
6847 /* the "[~]" will be removed by deformat_string */;
6849 else
6851 *value = NULL;
6857 if (check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
6858 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
6859 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
6860 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
6862 ERR("Invalid flags: %08x\n", *flags);
6863 return ERROR_FUNCTION_FAILED;
6866 if (!*flags)
6867 *flags = ENV_ACT_SETALWAYS | ENV_ACT_REMOVE;
6869 return ERROR_SUCCESS;
6872 static UINT open_env_key( DWORD flags, HKEY *key )
6874 static const WCHAR user_env[] =
6875 {'E','n','v','i','r','o','n','m','e','n','t',0};
6876 static const WCHAR machine_env[] =
6877 {'S','y','s','t','e','m','\\',
6878 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
6879 'C','o','n','t','r','o','l','\\',
6880 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
6881 'E','n','v','i','r','o','n','m','e','n','t',0};
6882 const WCHAR *env;
6883 HKEY root;
6884 LONG res;
6886 if (flags & ENV_MOD_MACHINE)
6888 env = machine_env;
6889 root = HKEY_LOCAL_MACHINE;
6891 else
6893 env = user_env;
6894 root = HKEY_CURRENT_USER;
6897 res = RegOpenKeyExW( root, env, 0, KEY_ALL_ACCESS, key );
6898 if (res != ERROR_SUCCESS)
6900 WARN("Failed to open key %s (%d)\n", debugstr_w(env), res);
6901 return ERROR_FUNCTION_FAILED;
6904 return ERROR_SUCCESS;
6907 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
6909 MSIPACKAGE *package = param;
6910 LPCWSTR name, value, component;
6911 LPWSTR data = NULL, newval = NULL, deformatted = NULL, ptr;
6912 DWORD flags, type, size;
6913 UINT res;
6914 HKEY env = NULL;
6915 MSICOMPONENT *comp;
6916 MSIRECORD *uirow;
6917 int action = 0;
6919 component = MSI_RecordGetString(rec, 4);
6920 comp = msi_get_loaded_component(package, component);
6921 if (!comp)
6922 return ERROR_SUCCESS;
6924 comp->Action = msi_get_component_action( package, comp );
6925 if (comp->Action != INSTALLSTATE_LOCAL)
6927 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
6928 return ERROR_SUCCESS;
6930 name = MSI_RecordGetString(rec, 2);
6931 value = MSI_RecordGetString(rec, 3);
6933 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
6935 res = env_parse_flags(&name, &value, &flags);
6936 if (res != ERROR_SUCCESS || !value)
6937 goto done;
6939 if (value && !deformat_string(package, value, &deformatted))
6941 res = ERROR_OUTOFMEMORY;
6942 goto done;
6945 value = deformatted;
6947 res = open_env_key( flags, &env );
6948 if (res != ERROR_SUCCESS)
6949 goto done;
6951 if (flags & ENV_MOD_MACHINE)
6952 action |= 0x20000000;
6954 size = 0;
6955 type = REG_SZ;
6956 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
6957 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
6958 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
6959 goto done;
6961 if ((res == ERROR_FILE_NOT_FOUND || !(flags & ENV_MOD_MASK)))
6963 action = 0x2;
6965 /* Nothing to do. */
6966 if (!value)
6968 res = ERROR_SUCCESS;
6969 goto done;
6972 /* If we are appending but the string was empty, strip ; */
6973 if ((flags & ENV_MOD_APPEND) && (value[0] == szSemiColon[0])) value++;
6975 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
6976 newval = strdupW(value);
6977 if (!newval)
6979 res = ERROR_OUTOFMEMORY;
6980 goto done;
6983 else
6985 action = 0x1;
6987 /* Contrary to MSDN, +-variable to [~];path works */
6988 if (flags & ENV_ACT_SETABSENT && !(flags & ENV_MOD_MASK))
6990 res = ERROR_SUCCESS;
6991 goto done;
6994 data = msi_alloc(size);
6995 if (!data)
6997 RegCloseKey(env);
6998 return ERROR_OUTOFMEMORY;
7001 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
7002 if (res != ERROR_SUCCESS)
7003 goto done;
7005 if (flags & ENV_ACT_REMOVEMATCH && (!value || !strcmpW( data, value )))
7007 action = 0x4;
7008 res = RegDeleteValueW(env, name);
7009 if (res != ERROR_SUCCESS)
7010 WARN("Failed to remove value %s (%d)\n", debugstr_w(name), res);
7011 goto done;
7014 size = (lstrlenW(data) + 1) * sizeof(WCHAR);
7015 if (flags & ENV_MOD_MASK)
7017 DWORD mod_size;
7018 int multiplier = 0;
7019 if (flags & ENV_MOD_APPEND) multiplier++;
7020 if (flags & ENV_MOD_PREFIX) multiplier++;
7021 mod_size = lstrlenW(value) * multiplier;
7022 size += mod_size * sizeof(WCHAR);
7025 newval = msi_alloc(size);
7026 ptr = newval;
7027 if (!newval)
7029 res = ERROR_OUTOFMEMORY;
7030 goto done;
7033 if (flags & ENV_MOD_PREFIX)
7035 lstrcpyW(newval, value);
7036 ptr = newval + lstrlenW(value);
7037 action |= 0x80000000;
7040 lstrcpyW(ptr, data);
7042 if (flags & ENV_MOD_APPEND)
7044 lstrcatW(newval, value);
7045 action |= 0x40000000;
7048 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
7049 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
7050 if (res)
7052 WARN("Failed to set %s to %s (%d)\n", debugstr_w(name), debugstr_w(newval), res);
7055 done:
7056 uirow = MSI_CreateRecord( 3 );
7057 MSI_RecordSetStringW( uirow, 1, name );
7058 MSI_RecordSetStringW( uirow, 2, newval );
7059 MSI_RecordSetInteger( uirow, 3, action );
7060 msi_ui_actiondata( package, szWriteEnvironmentStrings, uirow );
7061 msiobj_release( &uirow->hdr );
7063 if (env) RegCloseKey(env);
7064 msi_free(deformatted);
7065 msi_free(data);
7066 msi_free(newval);
7067 return res;
7070 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
7072 static const WCHAR query[] = {
7073 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7074 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7075 MSIQUERY *view;
7076 UINT rc;
7078 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
7079 if (rc != ERROR_SUCCESS)
7080 return ERROR_SUCCESS;
7082 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
7083 msiobj_release(&view->hdr);
7084 return rc;
7087 static UINT ITERATE_RemoveEnvironmentString( MSIRECORD *rec, LPVOID param )
7089 MSIPACKAGE *package = param;
7090 LPCWSTR name, value, component;
7091 LPWSTR deformatted = NULL;
7092 DWORD flags;
7093 HKEY env;
7094 MSICOMPONENT *comp;
7095 MSIRECORD *uirow;
7096 int action = 0;
7097 LONG res;
7098 UINT r;
7100 component = MSI_RecordGetString( rec, 4 );
7101 comp = msi_get_loaded_component( package, component );
7102 if (!comp)
7103 return ERROR_SUCCESS;
7105 comp->Action = msi_get_component_action( package, comp );
7106 if (comp->Action != INSTALLSTATE_ABSENT)
7108 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
7109 return ERROR_SUCCESS;
7111 name = MSI_RecordGetString( rec, 2 );
7112 value = MSI_RecordGetString( rec, 3 );
7114 TRACE("name %s value %s\n", debugstr_w(name), debugstr_w(value));
7116 r = env_parse_flags( &name, &value, &flags );
7117 if (r != ERROR_SUCCESS)
7118 return r;
7120 if (!(flags & ENV_ACT_REMOVE))
7122 TRACE("Environment variable %s not marked for removal\n", debugstr_w(name));
7123 return ERROR_SUCCESS;
7126 if (value && !deformat_string( package, value, &deformatted ))
7127 return ERROR_OUTOFMEMORY;
7129 value = deformatted;
7131 r = open_env_key( flags, &env );
7132 if (r != ERROR_SUCCESS)
7134 r = ERROR_SUCCESS;
7135 goto done;
7138 if (flags & ENV_MOD_MACHINE)
7139 action |= 0x20000000;
7141 TRACE("Removing %s\n", debugstr_w(name));
7143 res = RegDeleteValueW( env, name );
7144 if (res != ERROR_SUCCESS)
7146 WARN("Failed to delete value %s (%d)\n", debugstr_w(name), res);
7147 r = ERROR_SUCCESS;
7150 done:
7151 uirow = MSI_CreateRecord( 3 );
7152 MSI_RecordSetStringW( uirow, 1, name );
7153 MSI_RecordSetStringW( uirow, 2, value );
7154 MSI_RecordSetInteger( uirow, 3, action );
7155 msi_ui_actiondata( package, szRemoveEnvironmentStrings, uirow );
7156 msiobj_release( &uirow->hdr );
7158 if (env) RegCloseKey( env );
7159 msi_free( deformatted );
7160 return r;
7163 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
7165 static const WCHAR query[] = {
7166 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7167 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
7168 MSIQUERY *view;
7169 UINT rc;
7171 rc = MSI_DatabaseOpenViewW( package->db, query, &view );
7172 if (rc != ERROR_SUCCESS)
7173 return ERROR_SUCCESS;
7175 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveEnvironmentString, package );
7176 msiobj_release( &view->hdr );
7177 return rc;
7180 UINT msi_validate_product_id( MSIPACKAGE *package )
7182 LPWSTR key, template, id;
7183 UINT r = ERROR_SUCCESS;
7185 id = msi_dup_property( package->db, szProductID );
7186 if (id)
7188 msi_free( id );
7189 return ERROR_SUCCESS;
7191 template = msi_dup_property( package->db, szPIDTemplate );
7192 key = msi_dup_property( package->db, szPIDKEY );
7193 if (key && template)
7195 FIXME( "partial stub: template %s key %s\n", debugstr_w(template), debugstr_w(key) );
7196 r = msi_set_property( package->db, szProductID, key, -1 );
7198 msi_free( template );
7199 msi_free( key );
7200 return r;
7203 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
7205 return msi_validate_product_id( package );
7208 static UINT ACTION_ScheduleReboot( MSIPACKAGE *package )
7210 TRACE("\n");
7211 package->need_reboot_at_end = 1;
7212 return ERROR_SUCCESS;
7215 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
7217 static const WCHAR szAvailableFreeReg[] =
7218 {'A','V','A','I','L','A','B','L','E','F','R','E','E','R','E','G',0};
7219 MSIRECORD *uirow;
7220 int space = msi_get_property_int( package->db, szAvailableFreeReg, 0 );
7222 TRACE("%p %d kilobytes\n", package, space);
7224 uirow = MSI_CreateRecord( 1 );
7225 MSI_RecordSetInteger( uirow, 1, space );
7226 msi_ui_actiondata( package, szAllocateRegistrySpace, uirow );
7227 msiobj_release( &uirow->hdr );
7229 return ERROR_SUCCESS;
7232 static UINT ACTION_DisableRollback( MSIPACKAGE *package )
7234 TRACE("%p\n", package);
7236 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7237 return ERROR_SUCCESS;
7240 static UINT ACTION_InstallAdminPackage( MSIPACKAGE *package )
7242 FIXME("%p\n", package);
7243 return ERROR_SUCCESS;
7246 static UINT ACTION_SetODBCFolders( MSIPACKAGE *package )
7248 static const WCHAR driver_query[] = {
7249 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7250 'O','D','B','C','D','r','i','v','e','r',0};
7251 static const WCHAR translator_query[] = {
7252 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7253 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0};
7254 MSIQUERY *view;
7255 UINT r, count;
7257 r = MSI_DatabaseOpenViewW( package->db, driver_query, &view );
7258 if (r == ERROR_SUCCESS)
7260 count = 0;
7261 r = MSI_IterateRecords( view, &count, NULL, package );
7262 msiobj_release( &view->hdr );
7263 if (r != ERROR_SUCCESS)
7264 return r;
7265 if (count) FIXME("ignored %u rows in ODBCDriver table\n", count);
7267 r = MSI_DatabaseOpenViewW( package->db, translator_query, &view );
7268 if (r == ERROR_SUCCESS)
7270 count = 0;
7271 r = MSI_IterateRecords( view, &count, NULL, package );
7272 msiobj_release( &view->hdr );
7273 if (r != ERROR_SUCCESS)
7274 return r;
7275 if (count) FIXME("ignored %u rows in ODBCTranslator table\n", count);
7277 return ERROR_SUCCESS;
7280 static UINT ITERATE_RemoveExistingProducts( MSIRECORD *rec, LPVOID param )
7282 static const WCHAR fmtW[] =
7283 {'m','s','i','e','x','e','c',' ','/','i',' ','%','s',' ','R','E','M','O','V','E','=','%','s',0};
7284 MSIPACKAGE *package = param;
7285 const WCHAR *property = MSI_RecordGetString( rec, 7 );
7286 int attrs = MSI_RecordGetInteger( rec, 5 );
7287 UINT len = sizeof(fmtW)/sizeof(fmtW[0]);
7288 WCHAR *product, *features, *cmd;
7289 STARTUPINFOW si;
7290 PROCESS_INFORMATION info;
7291 BOOL ret;
7293 if (attrs & msidbUpgradeAttributesOnlyDetect) return ERROR_SUCCESS;
7294 if (!(product = msi_dup_property( package->db, property ))) return ERROR_SUCCESS;
7296 deformat_string( package, MSI_RecordGetString( rec, 6 ), &features );
7298 len += strlenW( product );
7299 if (features)
7300 len += strlenW( features );
7301 else
7302 len += sizeof(szAll) / sizeof(szAll[0]);
7304 if (!(cmd = msi_alloc( len * sizeof(WCHAR) )))
7306 msi_free( product );
7307 msi_free( features );
7308 return ERROR_OUTOFMEMORY;
7310 sprintfW( cmd, fmtW, product, features ? features : szAll );
7311 msi_free( product );
7312 msi_free( features );
7314 memset( &si, 0, sizeof(STARTUPINFOW) );
7315 ret = CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &info );
7316 msi_free( cmd );
7317 if (!ret) return GetLastError();
7318 CloseHandle( info.hThread );
7320 WaitForSingleObject( info.hProcess, INFINITE );
7321 CloseHandle( info.hProcess );
7322 return ERROR_SUCCESS;
7325 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
7327 static const WCHAR query[] = {
7328 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','U','p','g','r','a','d','e',0};
7329 MSIQUERY *view;
7330 UINT r;
7332 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7333 if (r == ERROR_SUCCESS)
7335 r = MSI_IterateRecords( view, NULL, ITERATE_RemoveExistingProducts, package );
7336 msiobj_release( &view->hdr );
7337 if (r != ERROR_SUCCESS)
7338 return r;
7340 return ERROR_SUCCESS;
7343 static UINT ITERATE_MigrateFeatureStates( MSIRECORD *rec, LPVOID param )
7345 MSIPACKAGE *package = param;
7346 int attributes = MSI_RecordGetInteger( rec, 5 );
7348 if (attributes & msidbUpgradeAttributesMigrateFeatures)
7350 const WCHAR *upgrade_code = MSI_RecordGetString( rec, 1 );
7351 const WCHAR *version_min = MSI_RecordGetString( rec, 2 );
7352 const WCHAR *version_max = MSI_RecordGetString( rec, 3 );
7353 const WCHAR *language = MSI_RecordGetString( rec, 4 );
7354 HKEY hkey;
7355 UINT r;
7357 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
7359 r = MSIREG_OpenClassesUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7360 if (r != ERROR_SUCCESS)
7361 return ERROR_SUCCESS;
7363 else
7365 r = MSIREG_OpenUserUpgradeCodesKey( upgrade_code, &hkey, FALSE );
7366 if (r != ERROR_SUCCESS)
7367 return ERROR_SUCCESS;
7369 RegCloseKey( hkey );
7371 FIXME("migrate feature states from %s version min %s version max %s language %s\n",
7372 debugstr_w(upgrade_code), debugstr_w(version_min),
7373 debugstr_w(version_max), debugstr_w(language));
7375 return ERROR_SUCCESS;
7378 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
7380 static const WCHAR query[] = {
7381 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7382 'U','p','g','r','a','d','e',0};
7383 MSIQUERY *view;
7384 UINT r;
7386 if (msi_get_property_int( package->db, szInstalled, 0 ))
7388 TRACE("product is installed, skipping action\n");
7389 return ERROR_SUCCESS;
7391 if (msi_get_property_int( package->db, szPreselected, 0 ))
7393 TRACE("Preselected property is set, not migrating feature states\n");
7394 return ERROR_SUCCESS;
7396 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7397 if (r == ERROR_SUCCESS)
7399 r = MSI_IterateRecords( view, NULL, ITERATE_MigrateFeatureStates, package );
7400 msiobj_release( &view->hdr );
7401 if (r != ERROR_SUCCESS)
7402 return r;
7404 return ERROR_SUCCESS;
7407 static void bind_image( const char *filename, const char *path )
7409 if (!BindImageEx( 0, filename, path, NULL, NULL ))
7411 WARN("failed to bind image %u\n", GetLastError());
7415 static UINT ITERATE_BindImage( MSIRECORD *rec, LPVOID param )
7417 UINT i;
7418 MSIFILE *file;
7419 MSIPACKAGE *package = param;
7420 const WCHAR *key = MSI_RecordGetString( rec, 1 );
7421 const WCHAR *paths = MSI_RecordGetString( rec, 2 );
7422 char *filenameA, *pathA;
7423 WCHAR *pathW, **path_list;
7425 if (!(file = msi_get_loaded_file( package, key )))
7427 WARN("file %s not found\n", debugstr_w(key));
7428 return ERROR_SUCCESS;
7430 if (!(filenameA = strdupWtoA( file->TargetPath ))) return ERROR_SUCCESS;
7431 path_list = msi_split_string( paths, ';' );
7432 if (!path_list) bind_image( filenameA, NULL );
7433 else
7435 for (i = 0; path_list[i] && path_list[i][0]; i++)
7437 deformat_string( package, path_list[i], &pathW );
7438 if ((pathA = strdupWtoA( pathW )))
7440 bind_image( filenameA, pathA );
7441 msi_free( pathA );
7443 msi_free( pathW );
7446 msi_free( path_list );
7447 msi_free( filenameA );
7448 return ERROR_SUCCESS;
7451 static UINT ACTION_BindImage( MSIPACKAGE *package )
7453 static const WCHAR query[] = {
7454 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7455 'B','i','n','d','I','m','a','g','e',0};
7456 MSIQUERY *view;
7457 UINT r;
7459 r = MSI_DatabaseOpenViewW( package->db, query, &view );
7460 if (r == ERROR_SUCCESS)
7462 r = MSI_IterateRecords( view, NULL, ITERATE_BindImage, package );
7463 msiobj_release( &view->hdr );
7464 if (r != ERROR_SUCCESS)
7465 return r;
7467 return ERROR_SUCCESS;
7470 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table )
7472 static const WCHAR query[] = {
7473 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','%','s','`',0};
7474 MSIQUERY *view;
7475 DWORD count = 0;
7476 UINT r;
7478 r = MSI_OpenQuery( package->db, &view, query, table );
7479 if (r == ERROR_SUCCESS)
7481 r = MSI_IterateRecords(view, &count, NULL, package);
7482 msiobj_release(&view->hdr);
7483 if (r != ERROR_SUCCESS)
7484 return r;
7486 if (count) FIXME("%s: ignored %u rows from %s\n", action, count, debugstr_w(table));
7487 return ERROR_SUCCESS;
7490 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
7492 static const WCHAR table[] = {
7493 'I','s','o','l','a','t','e','d','C','o','m','p','o','n','e','n','t',0 };
7494 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
7497 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
7499 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
7500 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
7503 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
7505 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7506 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
7509 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
7511 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
7512 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
7515 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
7517 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
7518 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
7521 static const struct
7523 const WCHAR *action;
7524 UINT (*handler)(MSIPACKAGE *);
7525 const WCHAR *action_rollback;
7527 StandardActions[] =
7529 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace, NULL },
7530 { szAppSearch, ACTION_AppSearch, NULL },
7531 { szBindImage, ACTION_BindImage, NULL },
7532 { szCCPSearch, ACTION_CCPSearch, NULL },
7533 { szCostFinalize, ACTION_CostFinalize, NULL },
7534 { szCostInitialize, ACTION_CostInitialize, NULL },
7535 { szCreateFolders, ACTION_CreateFolders, szRemoveFolders },
7536 { szCreateShortcuts, ACTION_CreateShortcuts, szRemoveShortcuts },
7537 { szDeleteServices, ACTION_DeleteServices, szInstallServices },
7538 { szDisableRollback, ACTION_DisableRollback, NULL },
7539 { szDuplicateFiles, ACTION_DuplicateFiles, szRemoveDuplicateFiles },
7540 { szExecuteAction, ACTION_ExecuteAction, NULL },
7541 { szFileCost, ACTION_FileCost, NULL },
7542 { szFindRelatedProducts, ACTION_FindRelatedProducts, NULL },
7543 { szForceReboot, ACTION_ForceReboot, NULL },
7544 { szInstallAdminPackage, ACTION_InstallAdminPackage, NULL },
7545 { szInstallExecute, ACTION_InstallExecute, NULL },
7546 { szInstallExecuteAgain, ACTION_InstallExecute, NULL },
7547 { szInstallFiles, ACTION_InstallFiles, szRemoveFiles },
7548 { szInstallFinalize, ACTION_InstallFinalize, NULL },
7549 { szInstallInitialize, ACTION_InstallInitialize, NULL },
7550 { szInstallODBC, ACTION_InstallODBC, szRemoveODBC },
7551 { szInstallServices, ACTION_InstallServices, szDeleteServices },
7552 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile, NULL },
7553 { szInstallValidate, ACTION_InstallValidate, NULL },
7554 { szIsolateComponents, ACTION_IsolateComponents, NULL },
7555 { szLaunchConditions, ACTION_LaunchConditions, NULL },
7556 { szMigrateFeatureStates, ACTION_MigrateFeatureStates, NULL },
7557 { szMoveFiles, ACTION_MoveFiles, NULL },
7558 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies, szMsiUnpublishAssemblies },
7559 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies, szMsiPublishAssemblies },
7560 { szPatchFiles, ACTION_PatchFiles, NULL },
7561 { szProcessComponents, ACTION_ProcessComponents, szProcessComponents },
7562 { szPublishComponents, ACTION_PublishComponents, szUnpublishComponents },
7563 { szPublishFeatures, ACTION_PublishFeatures, szUnpublishFeatures },
7564 { szPublishProduct, ACTION_PublishProduct, NULL },
7565 { szRegisterClassInfo, ACTION_RegisterClassInfo, szUnregisterClassInfo },
7566 { szRegisterComPlus, ACTION_RegisterComPlus, szUnregisterComPlus },
7567 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo, szUnregisterExtensionInfo },
7568 { szRegisterFonts, ACTION_RegisterFonts, szUnregisterFonts },
7569 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo, szUnregisterMIMEInfo },
7570 { szRegisterProduct, ACTION_RegisterProduct, NULL },
7571 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo, szUnregisterProgIdInfo },
7572 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries, szUnregisterTypeLibraries },
7573 { szRegisterUser, ACTION_RegisterUser, NULL },
7574 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles, szDuplicateFiles },
7575 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings, szWriteEnvironmentStrings },
7576 { szRemoveExistingProducts, ACTION_RemoveExistingProducts, NULL },
7577 { szRemoveFiles, ACTION_RemoveFiles, szInstallFiles },
7578 { szRemoveFolders, ACTION_RemoveFolders, szCreateFolders },
7579 { szRemoveIniValues, ACTION_RemoveIniValues, szWriteIniValues },
7580 { szRemoveODBC, ACTION_RemoveODBC, szInstallODBC },
7581 { szRemoveRegistryValues, ACTION_RemoveRegistryValues, szWriteRegistryValues },
7582 { szRemoveShortcuts, ACTION_RemoveShortcuts, szCreateShortcuts },
7583 { szResolveSource, ACTION_ResolveSource, NULL },
7584 { szRMCCPSearch, ACTION_RMCCPSearch, NULL },
7585 { szScheduleReboot, ACTION_ScheduleReboot, NULL },
7586 { szSelfRegModules, ACTION_SelfRegModules, szSelfUnregModules },
7587 { szSelfUnregModules, ACTION_SelfUnregModules, szSelfRegModules },
7588 { szSetODBCFolders, ACTION_SetODBCFolders, NULL },
7589 { szStartServices, ACTION_StartServices, szStopServices },
7590 { szStopServices, ACTION_StopServices, szStartServices },
7591 { szUnpublishComponents, ACTION_UnpublishComponents, szPublishComponents },
7592 { szUnpublishFeatures, ACTION_UnpublishFeatures, szPublishFeatures },
7593 { szUnregisterClassInfo, ACTION_UnregisterClassInfo, szRegisterClassInfo },
7594 { szUnregisterComPlus, ACTION_UnregisterComPlus, szRegisterComPlus },
7595 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo, szRegisterExtensionInfo },
7596 { szUnregisterFonts, ACTION_UnregisterFonts, szRegisterFonts },
7597 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo, szRegisterMIMEInfo },
7598 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo, szRegisterProgIdInfo },
7599 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries, szRegisterTypeLibraries },
7600 { szValidateProductID, ACTION_ValidateProductID, NULL },
7601 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings, szRemoveEnvironmentStrings },
7602 { szWriteIniValues, ACTION_WriteIniValues, szRemoveIniValues },
7603 { szWriteRegistryValues, ACTION_WriteRegistryValues, szRemoveRegistryValues },
7604 { NULL, NULL, NULL }
7607 static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
7609 BOOL ret = FALSE;
7610 UINT i;
7612 i = 0;
7613 while (StandardActions[i].action != NULL)
7615 if (!strcmpW( StandardActions[i].action, action ))
7617 ui_actionstart( package, action );
7618 if (StandardActions[i].handler)
7620 ui_actioninfo( package, action, TRUE, 0 );
7621 *rc = StandardActions[i].handler( package );
7622 ui_actioninfo( package, action, FALSE, *rc );
7624 if (StandardActions[i].action_rollback && !package->need_rollback)
7626 TRACE("scheduling rollback action\n");
7627 msi_schedule_action( package, SCRIPT_ROLLBACK, StandardActions[i].action_rollback );
7630 else
7632 FIXME("unhandled standard action %s\n", debugstr_w(action));
7633 *rc = ERROR_SUCCESS;
7635 ret = TRUE;
7636 break;
7638 i++;
7640 return ret;
7643 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7645 UINT rc = ERROR_SUCCESS;
7646 BOOL handled;
7648 TRACE("Performing action (%s)\n", debugstr_w(action));
7650 handled = ACTION_HandleStandardAction(package, action, &rc);
7652 if (!handled)
7653 handled = ACTION_HandleCustomAction(package, action, &rc, script, TRUE);
7655 if (!handled)
7657 WARN("unhandled msi action %s\n", debugstr_w(action));
7658 rc = ERROR_FUNCTION_NOT_CALLED;
7661 return rc;
7664 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
7666 UINT rc = ERROR_SUCCESS;
7667 BOOL handled = FALSE;
7669 TRACE("Performing action (%s)\n", debugstr_w(action));
7671 package->action_progress_increment = 0;
7672 handled = ACTION_HandleStandardAction(package, action, &rc);
7674 if (!handled)
7675 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
7677 if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
7678 handled = TRUE;
7680 if (!handled)
7682 WARN("unhandled msi action %s\n", debugstr_w(action));
7683 rc = ERROR_FUNCTION_NOT_CALLED;
7686 return rc;
7689 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq)
7691 UINT rc = ERROR_SUCCESS;
7692 MSIRECORD *row;
7694 static const WCHAR query[] =
7695 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7696 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
7697 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
7698 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
7699 static const WCHAR ui_query[] =
7700 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
7701 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
7702 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
7703 ' ', '=',' ','%','i',0};
7705 if (needs_ui_sequence(package))
7706 row = MSI_QueryGetRecord(package->db, ui_query, seq);
7707 else
7708 row = MSI_QueryGetRecord(package->db, query, seq);
7710 if (row)
7712 LPCWSTR action, cond;
7714 TRACE("Running the actions\n");
7716 /* check conditions */
7717 cond = MSI_RecordGetString(row, 2);
7719 /* this is a hack to skip errors in the condition code */
7720 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
7722 msiobj_release(&row->hdr);
7723 return ERROR_SUCCESS;
7726 action = MSI_RecordGetString(row, 1);
7727 if (!action)
7729 ERR("failed to fetch action\n");
7730 msiobj_release(&row->hdr);
7731 return ERROR_FUNCTION_FAILED;
7734 if (needs_ui_sequence(package))
7735 rc = ACTION_PerformUIAction(package, action, SCRIPT_NONE);
7736 else
7737 rc = ACTION_PerformAction(package, action, SCRIPT_NONE);
7739 msiobj_release(&row->hdr);
7742 return rc;
7745 /****************************************************
7746 * TOP level entry points
7747 *****************************************************/
7749 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
7750 LPCWSTR szCommandLine )
7752 static const WCHAR szDisableRollback[] = {'D','I','S','A','B','L','E','R','O','L','L','B','A','C','K',0};
7753 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
7754 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
7755 WCHAR *reinstall, *remove, *patch;
7756 BOOL ui_exists;
7757 UINT rc;
7759 msi_set_property( package->db, szAction, szInstall, -1 );
7761 package->script->InWhatSequence = SEQUENCE_INSTALL;
7763 if (szPackagePath)
7765 LPWSTR p, dir;
7766 LPCWSTR file;
7768 dir = strdupW(szPackagePath);
7769 p = strrchrW(dir, '\\');
7770 if (p)
7772 *(++p) = 0;
7773 file = szPackagePath + (p - dir);
7775 else
7777 msi_free(dir);
7778 dir = msi_alloc(MAX_PATH * sizeof(WCHAR));
7779 GetCurrentDirectoryW(MAX_PATH, dir);
7780 lstrcatW(dir, szBackSlash);
7781 file = szPackagePath;
7784 msi_free( package->PackagePath );
7785 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
7786 if (!package->PackagePath)
7788 msi_free(dir);
7789 return ERROR_OUTOFMEMORY;
7792 lstrcpyW(package->PackagePath, dir);
7793 lstrcatW(package->PackagePath, file);
7794 msi_free(dir);
7796 msi_set_sourcedir_props(package, FALSE);
7799 rc = msi_parse_command_line( package, szCommandLine, FALSE );
7800 if (rc != ERROR_SUCCESS)
7801 return rc;
7803 msi_apply_transforms( package );
7804 msi_apply_patches( package );
7806 patch = msi_dup_property( package->db, szPatch );
7807 remove = msi_dup_property( package->db, szRemove );
7808 reinstall = msi_dup_property( package->db, szReinstall );
7809 if (msi_get_property_int( package->db, szInstalled, 0 ) && !remove && !reinstall && !patch)
7811 TRACE("setting REINSTALL property to ALL\n");
7812 msi_set_property( package->db, szReinstall, szAll, -1 );
7813 package->full_reinstall = 1;
7816 /* properties may have been added by a transform */
7817 msi_clone_properties( package );
7818 msi_set_original_database_property( package->db, szPackagePath );
7820 msi_parse_command_line( package, szCommandLine, FALSE );
7821 msi_adjust_privilege_properties( package );
7822 msi_set_context( package );
7824 if (msi_get_property_int( package->db, szDisableRollback, 0 ))
7826 TRACE("disabling rollback\n");
7827 msi_set_property( package->db, szRollbackDisabled, szOne, -1 );
7830 if (needs_ui_sequence( package))
7832 package->script->InWhatSequence |= SEQUENCE_UI;
7833 rc = ACTION_ProcessUISequence(package);
7834 ui_exists = ui_sequence_exists(package);
7835 if (rc == ERROR_SUCCESS || !ui_exists)
7837 package->script->InWhatSequence |= SEQUENCE_EXEC;
7838 rc = ACTION_ProcessExecSequence(package, ui_exists);
7841 else
7842 rc = ACTION_ProcessExecSequence(package, FALSE);
7844 package->script->CurrentlyScripting = FALSE;
7846 /* process the ending type action */
7847 if (rc == ERROR_SUCCESS)
7848 ACTION_PerformActionSequence(package, -1);
7849 else if (rc == ERROR_INSTALL_USEREXIT)
7850 ACTION_PerformActionSequence(package, -2);
7851 else if (rc == ERROR_INSTALL_SUSPEND)
7852 ACTION_PerformActionSequence(package, -4);
7853 else /* failed */
7855 ACTION_PerformActionSequence(package, -3);
7856 if (!msi_get_property_int( package->db, szRollbackDisabled, 0 ))
7858 package->need_rollback = TRUE;
7862 /* finish up running custom actions */
7863 ACTION_FinishCustomActions(package);
7865 if (package->need_rollback && !reinstall)
7867 WARN("installation failed, running rollback script\n");
7868 execute_script( package, SCRIPT_ROLLBACK );
7870 msi_free( reinstall );
7871 msi_free( remove );
7872 msi_free( patch );
7874 if (rc == ERROR_SUCCESS && package->need_reboot_at_end)
7875 return ERROR_SUCCESS_REBOOT_REQUIRED;
7877 return rc;